├── config_override └── .placeholder ├── src ├── main │ ├── resources │ │ ├── version.properties │ │ ├── jau.properties │ │ └── logback.xml │ ├── assembly │ │ ├── uninstall.bat │ │ ├── jdk │ │ │ ├── unzip-provided-java.bat │ │ │ ├── unzip.exe │ │ │ ├── wget.exe │ │ │ ├── ntlmauth.dll │ │ │ ├── download-java.sh │ │ │ └── download-java.bat │ │ ├── config_override │ │ │ ├── application-env.properties │ │ │ ├── download-java-overrides.bat │ │ │ └── jau.properties │ │ ├── install.bat │ │ ├── environment-setup.bat │ │ └── assembly.xml │ └── java │ │ ├── no │ │ └── cantara │ │ │ └── jau │ │ │ ├── duplicatehandler │ │ │ ├── ProcessExecutorFetcher.java │ │ │ ├── UnixProcessExecutor.java │ │ │ ├── ProcessExecutor.java │ │ │ ├── LastRunningProcessFileUtil.java │ │ │ ├── WindowsProcessExecutor.java │ │ │ └── DuplicateProcessHandler.java │ │ │ ├── util │ │ │ ├── SleepUtil.java │ │ │ ├── ProxyFixer.java │ │ │ ├── AppConfig.java │ │ │ ├── ClientEnvironmentUtil.java │ │ │ ├── DisableSSLVerification.java │ │ │ └── PropertiesHelper.java │ │ │ ├── eventextraction │ │ │ ├── EventRepo.java │ │ │ ├── EventExtractorService.java │ │ │ ├── EventExtractor.java │ │ │ └── CommandExtractEventsFromFile.java │ │ │ ├── coms │ │ │ ├── RegisterClientHelper.java │ │ │ ├── CommandRegisterClient.java │ │ │ ├── RegisterClientExceptionHandler.java │ │ │ └── CheckForUpdateHelper.java │ │ │ ├── Main.java │ │ │ ├── ApplicationProcess.java │ │ │ └── JavaAutoUpdater.java │ │ └── org │ │ └── springframework │ │ └── util │ │ └── backoff │ │ ├── BackOffExecution.java │ │ ├── BackOff.java │ │ ├── FixedBackOff.java │ │ └── ExponentialBackOff.java └── test │ ├── resources │ └── unit-test.properties │ └── java │ └── no │ └── cantara │ └── jau │ ├── duplicatehandler │ ├── UnixProcessExecutorTest.java │ ├── DuplicateProcessHandlerIntTest.java │ └── DuplicateProcessHandlerTest.java │ ├── util │ ├── PropertiesHelperTest.java │ └── ClientEnvironmentUtilTest.java │ ├── coms │ ├── RegisterClientExceptionHandlerTest.java │ ├── CommandRegisterClientTest.java │ └── CheckForUpdateHelperTest.java │ ├── ApplicationProcessTest.java │ ├── eventextraction │ ├── EventExtractorServiceTest.java │ └── EventExtractorTest.java │ └── JAUProcessTest.java ├── renovate.json ├── .gitignore ├── .travis.yml ├── README.md ├── notinuse └── MainInProcess.java ├── LICENSE └── pom.xml /config_override/.placeholder: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/version.properties: -------------------------------------------------------------------------------- 1 | jau.version=${project.version} 2 | -------------------------------------------------------------------------------- /src/main/assembly/uninstall.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | bin\java-auto-update remove & pause 4 | -------------------------------------------------------------------------------- /src/main/assembly/jdk/unzip-provided-java.bat: -------------------------------------------------------------------------------- 1 | SET ZIP_FILE="java.zip" 2 | 3 | unzip.exe -q -o %ZIP_FILE% -------------------------------------------------------------------------------- /src/test/resources/unit-test.properties: -------------------------------------------------------------------------------- 1 | configservice.url=https://test.com 2 | configservice.username=ausername -------------------------------------------------------------------------------- /src/main/assembly/jdk/unzip.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cantara/Java-Auto-Update/HEAD/src/main/assembly/jdk/unzip.exe -------------------------------------------------------------------------------- /src/main/assembly/jdk/wget.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cantara/Java-Auto-Update/HEAD/src/main/assembly/jdk/wget.exe -------------------------------------------------------------------------------- /src/main/assembly/jdk/ntlmauth.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cantara/Java-Auto-Update/HEAD/src/main/assembly/jdk/ntlmauth.dll -------------------------------------------------------------------------------- /src/main/assembly/config_override/application-env.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Properties in this file will be sent as environment variables to the forked process. 3 | # -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "local>cantara/Cantara-Renovate-Config" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /src/main/assembly/install.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | :: Download java if java folder is missing 4 | if not exist java ( 5 | call download-java.bat 6 | ) 7 | 8 | bin\java-auto-update remove & bin\java-auto-update install & bin\java-auto-update start & sc failure java-auto-update reset= 60 actions= restart/10000/restart/10000/restart/10000 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.log 3 | *.serviceconfig 4 | dependency-reduced-pom.xml 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | target/ 11 | test-output/ 12 | 13 | #IDE files# 14 | .idea/ 15 | *.iml 16 | /.settings 17 | /.project 18 | /.classpath 19 | *.logg.properties 20 | 21 | # Unit Test Files # 22 | *overrides.properties 23 | -------------------------------------------------------------------------------- /src/main/assembly/jdk/download-java.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ZipFile=jdk.zip 3 | #-e use_proxy=yes -e http_proxy=10.246.5.10:8080 4 | wget -P . -O $ZipFile --referer=http://www.azulsystems.com/products/zulu/downloads http://cdn.azulsystems.com/zulu/2015-04-8.7-bin/zulu1.8.0_45-8.7.0.5-x86lx64.zip 5 | 6 | unzip -q -o $ZipFile 7 | mv zulu1.8.0_45-8.7.0.5-x86lx64 java 8 | rm $ZipFile 9 | echo 'zulu1.8.0_45-8.7.0.5-x86lx64 JDK downloaded to java folder' -------------------------------------------------------------------------------- /src/test/java/no/cantara/jau/duplicatehandler/UnixProcessExecutorTest.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau.duplicatehandler; 2 | 3 | import org.testng.annotations.Test; 4 | 5 | import java.io.IOException; 6 | 7 | public class UnixProcessExecutorTest { 8 | @Test 9 | public void shouldKillProcessByProcessName() throws IOException, InterruptedException { 10 | ProcessExecutor processExecutor = new UnixProcessExecutor(); 11 | 12 | processExecutor.killProcessByProcessName("1234"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/no/cantara/jau/duplicatehandler/ProcessExecutorFetcher.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau.duplicatehandler; 2 | 3 | public class ProcessExecutorFetcher { 4 | 5 | public ProcessExecutor getProcessExecutorBasedOnOs() { 6 | if (isWindows()) { 7 | return new WindowsProcessExecutor(); 8 | } else { 9 | return new UnixProcessExecutor(); 10 | } 11 | } 12 | 13 | private boolean isWindows() { 14 | return System.getProperty("os.name").toLowerCase().contains("windows"); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/assembly/environment-setup.bat: -------------------------------------------------------------------------------- 1 | 2 | rem Placeholder file for overriding properties in etc/wrapper.conf 3 | rem 4 | rem Set the WRAPPER_CONF_OVERRIDES environment variable to override wrapper properties. 5 | rem For example, to override the Windows service name, uncomment the following line: 6 | rem 7 | rem set WRAPPER_CONF_OVERRIDES=wrapper.ntservice.name=myagent wrapper.ntservice.displayname=myagent 8 | rem 9 | rem For more details, see: 10 | rem 11 | rem http://www.mojohaus.org/appassembler/appassembler-maven-plugin/generate-daemons-mojo.html#environmentSetupFileName 12 | rem http://wrapper.tanukisoftware.com/doc/english/props-command-line.html 13 | -------------------------------------------------------------------------------- /src/main/java/no/cantara/jau/util/SleepUtil.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau.util; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | public class SleepUtil { 7 | 8 | private static final Logger log = LoggerFactory.getLogger(SleepUtil.class); 9 | 10 | public static void sleepWithLogging(long waitInterval) { 11 | try { 12 | log.debug("retrying in {} milliseconds ", waitInterval); 13 | Thread.sleep(waitInterval); 14 | } catch (InterruptedException e) { 15 | log.error("Failed to run Thread.sleep({})", waitInterval); 16 | log.error(e.getMessage()); 17 | } 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/no/cantara/jau/eventextraction/EventRepo.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau.eventextraction; 2 | 3 | import java.util.ArrayList; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | 7 | import no.cantara.cs.dto.event.Event; 8 | 9 | /** 10 | * This class is thread-safe. 11 | */ 12 | public class EventRepo { 13 | private List events; 14 | 15 | public EventRepo() { 16 | this.events = new LinkedList<>(); 17 | } 18 | 19 | public synchronized void addEvents(List eventsToAdd) { 20 | events.addAll(eventsToAdd); 21 | } 22 | 23 | public synchronized List getEvents() { 24 | return new ArrayList<>(events); 25 | } 26 | 27 | public synchronized void clearEvents() { 28 | events.clear(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/assembly/config_override/download-java-overrides.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | :: 4 | :: Overrides properties in download-java.bat 5 | :: 6 | 7 | :: Download URL for the "normal" 64-bit JDK 8 | :: SET DOWNLOAD_URL= 9 | 10 | :: Download URL for the 32-bit JDK that will be downloaded on Windows 2003 servers 11 | :: SET DOWNLOAD_URL_32_BIT= 12 | 13 | :: Download URL for the optional cryptography extension kit, e.g., 14 | :: http://cdn.azul.com/zcek/bin/ZuluJCEPolicies.zip 15 | :: SET DOWNLOAD_URL_CRYPTO= 16 | 17 | :: Root directory in the downloaded zip file. Only relevant for the 64-bit JDK. 18 | :: SET ZIP_ROOT_DIR= 19 | 20 | :: Set this if using an http proxy. Specified as "host:port". 21 | :: SET HTTP_PROXY= 22 | 23 | :: Username and password for HTTP authentication. Only relevant for the 32-bit JDK. 24 | :: SET HTTP_USER= 25 | :: SET HTTP_PASSWORD= 26 | -------------------------------------------------------------------------------- /src/main/assembly/config_override/jau.properties: -------------------------------------------------------------------------------- 1 | #configservice.url=http://localhost:8086/jau/serviceconfig/query?clientid=clientid1 2 | configservice.url=http://localhost:8086/jau 3 | configservice.username=read 4 | configservice.password=wrongPassword 5 | configservice.artifactid=someArtifactId 6 | 7 | # Frequency of checking for updates (in seconds): 8 | updateinterval=180 9 | 10 | # Whether to stop the application process when JAU shuts down. 11 | stopApplicationOnShutdown=false 12 | 13 | # "startPattern" is a regex defining the start of a log entry. Setting this property causes multi-line 14 | # log entries to be collated before being sent to ConfigService. 15 | # For example, if your log entries starts with a UTC timestamp (e.g., 2016-06-09T14:01:05.348) you can 16 | # use the following regex: 17 | 18 | # startPattern=\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3} -------------------------------------------------------------------------------- /src/main/resources/jau.properties: -------------------------------------------------------------------------------- 1 | configservice.url=https://whydahdev.cantara.no/jau/client 2 | # configservice.url=http://localhost:8086/jau/client 3 | configservice.username=read 4 | configservice.password=baretillesing 5 | configservice.artifactid=cantara-demo 6 | 7 | updateinterval=60 8 | isrunninginterval=40 9 | 10 | clientName=local-jau 11 | 12 | monitor.events=testkey 13 | 14 | # Start a subprocess if the PID file cannot be read. May lead to duplicate subprocesses running if this 15 | # is set to true 16 | forceStartSubProcess=false 17 | 18 | # "startPattern" is a regex defining the start of a log entry. Setting this property causes multi-line 19 | # log entries to be collated before being sent to ConfigService. 20 | # For example, if your log entries starts with a UTC timestamp (e.g., 2016-06-09T14:01:05.348) you can 21 | # use the following regex: 22 | 23 | # startPattern=\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3} 24 | 25 | # Specify a list of domains (separated by a comma) that TLS check should be disabled for. Matches the Subject DN of the 26 | # certificate the given domain, so if it is a wildcard certificate, specify the domain as e.g. "*.example.com" 27 | disable.tlscheck.domains="" -------------------------------------------------------------------------------- /src/main/java/org/springframework/util/backoff/BackOffExecution.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002-2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.util.backoff; 18 | 19 | /** 20 | * Represent a particular back-off execution. 21 | * 22 | *

Implementations do not need to be thread safe. 23 | * 24 | * @author Stephane Nicoll 25 | * @since 4.1 26 | * @see BackOff 27 | */ 28 | public interface BackOffExecution { 29 | 30 | /** 31 | * Return value of {@link #nextBackOff()} that indicates that the operation 32 | * should not be retried. 33 | */ 34 | long STOP = -1; 35 | 36 | /** 37 | * Return the number of milliseconds to wait before retrying the operation 38 | * or {@link #STOP} ({@value #STOP}) to indicate that no further attempt 39 | * should be made for the operation. 40 | */ 41 | long nextBackOff(); 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | %d{yyyy-MM-dd'T'HH:mm:ss.SSSZ} [%thread] %-5level %logger{35} - %msg%n 10 | 11 | 12 | 13 | 14 | ${LOG_DIR}${appName}.log 15 | 16 | 17 | ${LOG_DIR}${appName}-%d{yyyy-MM-dd}.%i.log 18 | 5 19 | 1GB 20 | 50MB 21 | 22 | 23 | %d{yyyy-MM-dd'T'HH:mm:ss.SSSZ} [%thread] %-5level %logger{35} - %msg%n 24 | UTF-8 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/main/java/no/cantara/jau/duplicatehandler/UnixProcessExecutor.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau.duplicatehandler; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.io.IOException; 7 | import java.lang.reflect.Field; 8 | 9 | public class UnixProcessExecutor extends ProcessExecutor { 10 | private static final Logger log = LoggerFactory.getLogger(UnixProcessExecutor.class); 11 | 12 | @Override 13 | public boolean killProcessByPID(String pid) throws IOException, InterruptedException { 14 | ProcessBuilder processBuilder = new ProcessBuilder("kill", "-9", pid); 15 | return executeProcess(processBuilder); 16 | } 17 | 18 | @Override 19 | public boolean killProcessByProcessName(String processName) throws IOException, InterruptedException { 20 | //TODO: Implementation. E.g. use ps aux and grep for command to get right java-process to kill 21 | return false; 22 | } 23 | 24 | @Override 25 | public boolean isProcessRunning(String pid) throws IOException, InterruptedException { 26 | ProcessBuilder processBuilder; 27 | processBuilder = new ProcessBuilder("ps", "-p", pid); 28 | return executeProcess(processBuilder); 29 | } 30 | 31 | @Override 32 | public String findProcessId(Process process) throws NoSuchFieldException, IllegalAccessException { 33 | String pid; 34 | Field pidField; 35 | pidField = process.getClass().getDeclaredField("pid"); 36 | pidField.setAccessible(true); 37 | pid = Long.toString(pidField.getLong(process)); 38 | return pid; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/no/cantara/jau/coms/RegisterClientHelper.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau.coms; 2 | 3 | import com.netflix.hystrix.exception.HystrixRuntimeException; 4 | import no.cantara.cs.client.ConfigServiceClient; 5 | import no.cantara.cs.dto.ClientConfig; 6 | import org.springframework.util.backoff.BackOff; 7 | import org.springframework.util.backoff.BackOffExecution; 8 | import org.springframework.util.backoff.ExponentialBackOff; 9 | 10 | public class RegisterClientHelper { 11 | 12 | private final ConfigServiceClient configServiceClient; 13 | private String clientId; 14 | private final String artifactId; 15 | private final String clientName; 16 | 17 | public RegisterClientHelper(ConfigServiceClient configServiceClient, String artifactId, String clientName, String clientId) { 18 | this.artifactId = artifactId; 19 | this.clientName = clientName; 20 | this.configServiceClient = configServiceClient; 21 | this.clientId = clientId; 22 | } 23 | 24 | public ClientConfig registerClient() { 25 | BackOff exponentialBackOff = new ExponentialBackOff(); 26 | BackOffExecution backOffExecution = exponentialBackOff.start(); 27 | 28 | while (true) { 29 | try { 30 | return new CommandRegisterClient(artifactId, configServiceClient, clientName, clientId).execute(); 31 | } catch (HystrixRuntimeException e) { 32 | RegisterClientExceptionHandler.handleRegisterClientException(e, exponentialBackOff, backOffExecution, 33 | configServiceClient.getUrl()); 34 | } 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | language: java 3 | # We now cache the Maven dependencies directory for faster builds 4 | cache: 5 | apt: true 6 | directories: 7 | - $HOME/.m2 8 | - $HOME/.downloads 9 | sudo: true 10 | 11 | #notifications: 12 | # slack: cantara:Mc0ZrDXc2tFgnWeDd5xwiTGn 13 | 14 | addons: 15 | apt: 16 | sources: 17 | - sourceline: 'deb http://repos.azulsystems.com/ubuntu stable main' 18 | key_url: 'http://repos.azulsystems.com/RPM-GPG-KEY-azulsystems' 19 | packages: 20 | - zulu-8 21 | 22 | 23 | before_install: 24 | - echo 'MAVEN_OPTS="-Dorg.slf4j.simpleLogger.defaultLogLevel=warn"' >~/.mavenrc 25 | - mkdir -p $HOME/.downloads 26 | - wget -N -q -O $HOME/.downloads/ZuluJCEPolicies.zip 'https://cdn.azul.com/zcek/bin/ZuluJCEPolicies.zip' 27 | # # The SHA256 checksum for JCE for Azul will break if Azul updates their archive. 28 | # # If so, you will need to update the fingerprint below after verifying the 29 | # # authenticity of the archive. 30 | # - echo "8021a28b8cac41b44f1421fd210a0a0822fcaf88d62d2e70a35b2ff628a8675a $HOME/.downloads/ZuluJCEPolicies.zip" | sha256sum -c 31 | # - sudo unzip -o -j $HOME/.downloads/ZuluJCEPolicies.zip ZuluJCEPolicies/local_policy.jar ZuluJCEPolicies/US_export_policy.jar -d /usr/lib/jvm/zulu-8-amd64/jre/lib/security 32 | # - sudo unzip -o -j $HOME/.downloads/ZuluJCEPolicies.zip ZuluJCEPolicies/local_policy.jar ZuluJCEPolicies/US_export_policy.jar -d /usr/lib/jvm/zulu-9-amd64/lib/security 33 | 34 | matrix: 35 | fast_finish: true 36 | include: 37 | # unit test build tests (zulu-8) 38 | - env: 39 | - JAVA_HOME=/usr/lib/jvm/zulu-8-amd64 40 | - DESC="zulu-8 unit tests" 41 | - CMD="mvn clean test -Dcheckstyle.skip=true" 42 | - LANG=en_US.utf8 43 | 44 | script: echo ${CMD}; ${CMD} -------------------------------------------------------------------------------- /src/main/java/org/springframework/util/backoff/BackOff.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002-2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.util.backoff; 18 | 19 | /** 20 | * Provide a {@link BackOffExecution} that indicates the rate at which 21 | * an operation should be retried. 22 | * 23 | *

Users of this interface are expected to use it like this: 24 | * 25 | *

26 |  * BackOffExecution exec = backOff.start();
27 |  *
28 |  * // In the operation recovery/retry loop:
29 |  * long waitInterval = exec.nextBackOffMillis();
30 |  * if (waitInterval == BackOffExecution.STOP) {
31 |  *     // do not retry operation
32 |  * }
33 |  * else {
34 |  *     // sleep, e.g. Thread.sleep(waitInterval)
35 |  *     // retry operation
36 |  * }
37 |  * }
38 | * 39 | * Once the underlying operation has completed successfully, 40 | * the execution instance can be simply discarded. 41 | * 42 | * @author Stephane Nicoll 43 | * @since 4.1 44 | * @see BackOffExecution 45 | */ 46 | public interface BackOff { 47 | 48 | /** 49 | * Start a new back off execution. 50 | * @return a fresh {@link BackOffExecution} ready to be used 51 | */ 52 | BackOffExecution start(); 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/no/cantara/jau/duplicatehandler/ProcessExecutor.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau.duplicatehandler; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.io.BufferedReader; 7 | import java.io.IOException; 8 | import java.io.InputStreamReader; 9 | 10 | public abstract class ProcessExecutor { 11 | private static final Logger log = LoggerFactory.getLogger(ProcessExecutor.class); 12 | 13 | abstract public boolean killProcessByPID(String pid) throws IOException, InterruptedException; 14 | 15 | public abstract boolean killProcessByProcessName(String processName) throws IOException, InterruptedException; 16 | 17 | abstract public boolean isProcessRunning(String pid) throws IOException, InterruptedException; 18 | 19 | abstract public String findProcessId(Process process) throws ReflectiveOperationException; 20 | 21 | static boolean executeProcess(ProcessBuilder processBuilder) throws IOException, InterruptedException { 22 | Process p = processBuilder.start(); 23 | p.waitFor(); 24 | if (p.exitValue() == 0) { 25 | return true; 26 | } else { 27 | printErrorCommandFromProcess(p); 28 | } 29 | return false; 30 | } 31 | 32 | static void printErrorCommandFromProcess(Process p) throws IOException { 33 | BufferedReader reader = 34 | new BufferedReader(new InputStreamReader(p.getErrorStream())); 35 | StringBuilder builder = new StringBuilder(); 36 | String line; 37 | while ((line = reader.readLine()) != null) { 38 | builder.append(line); 39 | } 40 | String result = builder.toString(); 41 | if (!result.isEmpty()) { 42 | log.error("Error output from kill command: '{}'", result); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/no/cantara/jau/coms/CommandRegisterClient.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau.coms; 2 | 3 | import com.netflix.hystrix.HystrixCommand; 4 | import com.netflix.hystrix.HystrixCommandGroupKey; 5 | import com.netflix.hystrix.HystrixCommandProperties; 6 | import no.cantara.cs.client.ConfigServiceClient; 7 | import no.cantara.cs.dto.ClientConfig; 8 | import no.cantara.cs.dto.ClientRegistrationRequest; 9 | import no.cantara.jau.util.ClientEnvironmentUtil; 10 | 11 | import java.io.IOException; 12 | 13 | public class CommandRegisterClient extends HystrixCommand { 14 | 15 | private static final int COMMAND_TIMEOUT = 5000; 16 | private static final String GROUP_KEY = "GROUP_KEY"; 17 | private final String artifactId; 18 | private ConfigServiceClient configServiceClient; 19 | private final String clientName; 20 | private String clientId; 21 | 22 | public CommandRegisterClient(String artifactId, ConfigServiceClient configServiceClient, String clientName, String clientId) { 23 | super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(GROUP_KEY)) 24 | .andCommandPropertiesDefaults(HystrixCommandProperties.Setter() 25 | .withExecutionTimeoutInMilliseconds(COMMAND_TIMEOUT))); 26 | this.artifactId = artifactId; 27 | this.configServiceClient = configServiceClient; 28 | this.clientName = clientName; 29 | this.clientId = clientId; 30 | } 31 | 32 | @Override 33 | protected ClientConfig run() throws IOException { 34 | ClientRegistrationRequest registrationRequest = new ClientRegistrationRequest(artifactId); 35 | registrationRequest.envInfo.putAll(ClientEnvironmentUtil.getClientEnvironment()); 36 | registrationRequest.clientName = clientName; 37 | registrationRequest.clientId = clientId; 38 | 39 | ClientConfig clientConfig = configServiceClient.registerClient(registrationRequest); 40 | 41 | configServiceClient.saveApplicationState(clientConfig); 42 | return clientConfig; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/no/cantara/jau/duplicatehandler/LastRunningProcessFileUtil.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau.duplicatehandler; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.io.*; 7 | import java.nio.file.Files; 8 | import java.nio.file.Path; 9 | import java.nio.file.Paths; 10 | 11 | public class LastRunningProcessFileUtil { 12 | private static final Logger log = LoggerFactory.getLogger(LastRunningProcessFileUtil.class); 13 | private String fileName; 14 | 15 | public LastRunningProcessFileUtil(String fileName) { 16 | this.fileName = fileName; 17 | } 18 | 19 | public String getFileName() { 20 | return fileName; 21 | } 22 | 23 | public void writePidToFile(String pid) { 24 | Path filePath = Paths.get(fileName); 25 | try { 26 | if (!Files.exists(filePath)) { 27 | Files.createFile(Paths.get(fileName)); 28 | } 29 | } catch (IOException e) { 30 | log.error("Could not create file to managed process pid={}", pid, e); 31 | return; 32 | } 33 | 34 | try (Writer writer = new BufferedWriter(new OutputStreamWriter( 35 | new FileOutputStream(fileName), "utf-8"))) { 36 | writer.write(pid); 37 | log.debug("Wrote pid={} to file={}", pid, fileName); 38 | } catch (FileNotFoundException e) { 39 | log.error("File '{}' to write managed process pid={} not found", fileName, pid, e); 40 | } catch (UnsupportedEncodingException e) { 41 | log.error("Encoding error while writing to {}", fileName, e); 42 | } catch (IOException e) { 43 | log.error("Could not write to file '{}'", fileName, e); 44 | } 45 | } 46 | 47 | public String getRunningProcessPidFromFile() throws IOException { 48 | Path file = Paths.get(fileName); 49 | String pid = null; 50 | if (Files.exists(file)) { 51 | pid = new String(Files.readAllBytes(Paths.get(fileName))); 52 | } 53 | return pid; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/no/cantara/jau/util/ProxyFixer.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau.util; 2 | 3 | import java.util.Properties; 4 | 5 | public class ProxyFixer { 6 | 7 | public static void fixProxy(Properties properties) { 8 | Boolean useProxy = getBooleanProperty(properties, "http.useProxy", false); 9 | if (useProxy) { 10 | setProxy(properties); 11 | } 12 | } 13 | 14 | private static Boolean getBooleanProperty(final Properties properties, String propertyKey, Boolean defaultValue) { 15 | String property = getStringProperty(properties, propertyKey, null); 16 | if (property == null) { 17 | return defaultValue; 18 | } 19 | return Boolean.valueOf(property); 20 | } 21 | 22 | private static String getStringProperty(final Properties properties, String propertyKey, String defaultValue) { 23 | String property = properties.getProperty(propertyKey, defaultValue); 24 | if (property == null) { 25 | property = System.getProperty(propertyKey); 26 | } 27 | return property; 28 | } 29 | 30 | public static void setProxy(Properties properties) { 31 | System.setProperty("http.proxyHost", getHTTPHost(properties)); 32 | System.setProperty("http.proxyPort", getHTTPPort(properties)); 33 | System.setProperty("https.proxyHost", getHTTPSHost(properties)); 34 | System.setProperty("https.proxyPort", getHTTPSPort(properties)); 35 | 36 | } 37 | 38 | private static String getHTTPPort(Properties properties) { 39 | return getStringProperty(properties, "http.proxyPort", null); 40 | } 41 | 42 | private static String getHTTPHost(Properties properties) { 43 | return getStringProperty(properties, "http.proxyHost", null); 44 | } 45 | 46 | private static String getHTTPSHost(Properties properties) { 47 | return getStringProperty(properties, "https.proxyHost", null); 48 | } 49 | 50 | private static String getHTTPSPort(Properties properties) { 51 | return getStringProperty(properties, "https.proxyPort", null); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/assembly/assembly.xml: -------------------------------------------------------------------------------- 1 | 4 | zip 5 | 6 | zip 7 | 8 | 9 | true 10 | 11 | 12 | 13 | 14 | ${project.build.directory}/jsw/${project.artifactId} 15 | 16 | bin/* 17 | etc/* 18 | lib/wrapper.jar 19 | lib/*.dll 20 | 21 | 22 | 23 | 24 | 25 | 26 | ${project.build.directory} 27 | 28 | ${project.build.finalName}.jar 29 | 30 | lib 31 | 32 | 33 | 34 | src/main/assembly/config_override 35 | config_override 36 | 37 | 38 | 39 | src/main/assembly/jdk 40 | 41 | 42 | 43 | 44 | src/main/assembly 45 | 46 | install.bat 47 | uninstall.bat 48 | 49 | 50 | 51 | 52 | 53 | src/main/assembly 54 | 55 | environment-setup.bat 56 | 57 | bin 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/main/java/no/cantara/jau/duplicatehandler/WindowsProcessExecutor.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau.duplicatehandler; 2 | 3 | import com.sun.jna.Pointer; 4 | import com.sun.jna.platform.win32.Kernel32; 5 | import com.sun.jna.platform.win32.WinNT; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.io.IOException; 10 | import java.lang.reflect.Field; 11 | 12 | public class WindowsProcessExecutor extends ProcessExecutor { 13 | private static final Logger log = LoggerFactory.getLogger(WindowsProcessExecutor.class); 14 | 15 | @Override 16 | public boolean killProcessByPID(String pid) throws IOException, InterruptedException { 17 | ProcessBuilder processBuilder = new ProcessBuilder("C:\\Windows\\System32\\taskkill.exe", "/pid", pid, "/f"); 18 | return executeProcess(processBuilder); 19 | } 20 | 21 | @Override 22 | public boolean killProcessByProcessName(String processName) throws IOException, InterruptedException { 23 | //TODO: Implementation. E.g. use wmic to get 'commandline' and filter for right java-process to kill 24 | return false; 25 | } 26 | 27 | @Override 28 | public boolean isProcessRunning(String pid) throws IOException, InterruptedException { 29 | ProcessBuilder processBuilder; 30 | processBuilder = new ProcessBuilder("C:\\Windows\\System32\\cmd.exe", "/c", "C:\\Windows\\System32\\tasklist.exe", 31 | "/FI", "\"PID eq " + pid + "\" | C:\\Windows\\System32\\findstr.exe " + pid + "\""); 32 | return executeProcess(processBuilder); 33 | } 34 | 35 | @Override 36 | public String findProcessId(Process process) throws NoSuchFieldException, IllegalAccessException { 37 | if (process.getClass().getName().equals("java.lang.Win32Process") 38 | || process.getClass().getName().equals("java.lang.ProcessImpl")) { 39 | Field f = process.getClass().getDeclaredField("handle"); 40 | f.setAccessible(true); 41 | long handleNumber = f.getLong(process); 42 | 43 | Kernel32 kernel = Kernel32.INSTANCE; 44 | WinNT.HANDLE handle = new WinNT.HANDLE(); 45 | handle.setPointer(Pointer.createConstant(handleNumber)); 46 | int pid = kernel.GetProcessId(handle); 47 | log.debug("Found pid for managed process: {}", pid); 48 | return pid + ""; 49 | } 50 | return null; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/no/cantara/jau/util/AppConfig.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau.util; 2 | 3 | import org.constretto.ConstrettoBuilder; 4 | import org.constretto.ConstrettoConfiguration; 5 | import org.constretto.model.Resource; 6 | import org.slf4j.Logger; 7 | 8 | import java.io.File; 9 | import java.util.Map; 10 | 11 | import static org.slf4j.LoggerFactory.getLogger; 12 | 13 | /** 14 | * Created by baardl on 17.06.15. 15 | */ 16 | public class AppConfig { 17 | private static final Logger log = getLogger(AppConfig.class); 18 | final ConstrettoConfiguration configuration; 19 | 20 | private static volatile AppConfig instance; 21 | 22 | 23 | private AppConfig(String appname) { 24 | if (appname == null) { 25 | throw new IllegalArgumentException("appname is null. Please run AppName.init(\"myawesomeapp\") before reading configuration"); 26 | } 27 | String appnameDotProperties = "classpath:" + appname + ".properties"; 28 | String appnameOverridesDotProperties = "file:" + System.getProperty("user.dir")+ 29 | File.separator + "config_override"+ 30 | File.separator + appname + 31 | "_overrides.properties"; 32 | System.out.println("Properties Loaded from: Commandline - "+ appnameOverridesDotProperties+ " - "+appnameDotProperties ); 33 | configuration = new ConstrettoBuilder() 34 | .createPropertiesStore() 35 | .addResource(Resource.create(appnameDotProperties)) 36 | .addResource(Resource.create(appnameOverridesDotProperties)) 37 | .done() 38 | .getConfiguration(); 39 | printConfiguration(configuration); 40 | } 41 | 42 | public static String getString(String key) { 43 | try { 44 | return getInstance().configuration.evaluateToString(key); 45 | } catch (Exception e) { 46 | return null; 47 | } 48 | } 49 | public static void init(String appname) { 50 | instance = new AppConfig(appname); 51 | getInstance(); 52 | } 53 | 54 | public static AppConfig getInstance() { 55 | return instance; 56 | } 57 | 58 | private static void printConfiguration(ConstrettoConfiguration configuration) { 59 | Map properties = configuration.asMap(); 60 | for (String key : properties.keySet()) { 61 | log.info("Loading Property: {}, value: {}", key, properties.get(key)); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /src/test/java/no/cantara/jau/util/PropertiesHelperTest.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau.util; 2 | 3 | import java.io.File; 4 | import java.util.Map; 5 | import java.util.Properties; 6 | import java.io.BufferedWriter; 7 | import java.io.FileWriter; 8 | import java.io.IOException; 9 | 10 | import org.testng.annotations.Test; 11 | 12 | import static org.testng.Assert.assertEquals; 13 | import static org.testng.Assert.assertTrue; 14 | 15 | /** 16 | * Created by jorunfa on 11/11/15. 17 | */ 18 | public class PropertiesHelperTest { 19 | 20 | @Test 21 | public void testGetVersion() { 22 | String version = PropertiesHelper.getVersion(); 23 | 24 | assertTrue(version.contains(".")); 25 | } 26 | 27 | @Test 28 | public void testPropertiesAsMap() { 29 | Properties properties = new Properties(); 30 | properties.put("key1", "value1"); 31 | properties.put("key2", "value2"); 32 | properties.put("key3", 42); 33 | 34 | Map map = PropertiesHelper.propertiesAsMap(properties); 35 | assertEquals(3, map.size()); 36 | assertEquals("value1", map.get("key1")); 37 | assertEquals("value2", map.get("key2")); 38 | assertEquals("42", map.get("key3")); 39 | } 40 | @Test 41 | public void testPropertiesPrecedence(){ 42 | String filename2 = System.getProperty("user.dir")+ File.separator + "config_override" + File.separator + "unit-test_overrides.properties"; 43 | BufferedWriter bw2 = null; 44 | FileWriter fw2 = null; 45 | try { 46 | String content = "configservice.url=https://overridetest.com"+System.getProperty("line.separator"); 47 | fw2 = new FileWriter(filename2); 48 | bw2 = new BufferedWriter(fw2); 49 | bw2.write(content); 50 | } catch (IOException e) { 51 | e.printStackTrace(); 52 | } finally { 53 | try { 54 | if (bw2 != null) 55 | bw2.close(); 56 | if (fw2 != null) 57 | fw2.close(); 58 | } catch (IOException ex) { 59 | ex.printStackTrace(); 60 | } 61 | } 62 | AppConfig.init("unit-test"); 63 | PropertiesHelper.getPropertiesFromConfigFile(PropertiesHelper.JAU_CONFIG_FILENAME); 64 | assertEquals(PropertiesHelper.getConfigServiceUrl(),"https://overridetest.com"); 65 | assertEquals(PropertiesHelper.getUsername(),"ausername"); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/no/cantara/jau/coms/RegisterClientExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau.coms; 2 | 3 | import com.netflix.hystrix.exception.HystrixRuntimeException; 4 | import no.cantara.jau.util.SleepUtil; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.util.backoff.BackOff; 8 | import org.springframework.util.backoff.BackOffExecution; 9 | import org.springframework.util.backoff.ExponentialBackOff; 10 | 11 | import javax.ws.rs.BadRequestException; 12 | import javax.ws.rs.InternalServerErrorException; 13 | import javax.ws.rs.NotFoundException; 14 | import java.net.ConnectException; 15 | import java.util.concurrent.TimeoutException; 16 | 17 | public class RegisterClientExceptionHandler { 18 | 19 | private static final Logger log = LoggerFactory.getLogger(RegisterClientHelper.class); 20 | 21 | public static void handleRegisterClientException(HystrixRuntimeException e, BackOff exponentialBackOff, 22 | BackOffExecution backOffExecution, String configServiceUrl) { 23 | Throwable cause = e.getCause(); 24 | log.debug("Exception registering client, exception getMessage={}", e.getMessage()); 25 | log.debug("Exception registering client, cause getMessage={}", cause.getMessage()); 26 | 27 | if (cause instanceof ConnectException) { 28 | log.debug("Connection refused to ConfigService url={}", configServiceUrl); 29 | } else if (cause instanceof InternalServerErrorException) { 30 | log.debug("Internal server error in ConfigService url={}", configServiceUrl); 31 | } else if(cause instanceof NotFoundException) { 32 | log.debug("404 not found to ConfigService url={}", configServiceUrl); 33 | } else if (cause instanceof BadRequestException) { 34 | log.error("400 Bad Request. Probably need to fix something on the client. Exiting after a" + 35 | " wait, so as to not DDoS the server."); 36 | 37 | // TODO Do a sensible BackOff implementation class comparissmnet before this!!! 38 | SleepUtil.sleepWithLogging(((ExponentialBackOff)exponentialBackOff).getMaxInterval() * 2); 39 | System.exit(1); 40 | } else if (cause instanceof TimeoutException) { 41 | log.debug("CommandRegisterClient timed out."); 42 | } else { 43 | log.error("Couldn't handle exception: {}", e); 44 | } 45 | 46 | SleepUtil.sleepWithLogging(backOffExecution.nextBackOff()); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/no/cantara/jau/eventextraction/EventExtractorService.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau.eventextraction; 2 | 3 | import no.cantara.cs.client.EventExtractionUtil; 4 | import no.cantara.cs.dto.event.Event; 5 | import no.cantara.cs.dto.event.EventExtractionConfig; 6 | import no.cantara.cs.dto.event.EventExtractionTag; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Collections; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.concurrent.ExecutorService; 15 | import java.util.concurrent.Executors; 16 | import java.util.concurrent.Future; 17 | 18 | public class EventExtractorService { 19 | private static final Logger log = LoggerFactory.getLogger(EventExtractorService.class); 20 | private final EventRepo repo; 21 | private final String startPattern; 22 | private List eventExtractors = Collections.emptyList(); 23 | private final ExecutorService executor; 24 | 25 | public EventExtractorService(EventRepo repo, String startPattern) { 26 | this.repo = repo; 27 | this.startPattern = startPattern; 28 | this.executor = Executors.newCachedThreadPool(); 29 | } 30 | 31 | public void updateConfigs(List configs) { 32 | createEventExtractors(configs); 33 | } 34 | 35 | private List> runEventExtractors() { 36 | try { 37 | return executor.invokeAll(eventExtractors); 38 | } catch (InterruptedException e) { 39 | log.error("Execution of EventExtractor was interrupted!", e); 40 | } 41 | return null; 42 | } 43 | 44 | private void createEventExtractors(List configs) { 45 | log.info(configs.toString()); 46 | eventExtractors = new ArrayList<>(); 47 | for (EventExtractionConfig config : configs) { 48 | Map> tagsByFile = EventExtractionUtil 49 | .groupExtractionConfigsByFile(config); 50 | 51 | for (String filePath : tagsByFile.keySet()) { 52 | List eventExtractionTags = tagsByFile.get(filePath); 53 | EventExtractor extractor = new EventExtractor(repo, eventExtractionTags, filePath, config.groupName, startPattern); 54 | eventExtractors.add(extractor); 55 | } 56 | } 57 | log.debug("Created {} EventExtractors", eventExtractors.size()); 58 | } 59 | 60 | public List extractEvents() { 61 | log.debug("Extracting events."); 62 | runEventExtractors(); 63 | return repo.getEvents(); 64 | } 65 | 66 | public void clearRepo() { 67 | repo.clearEvents(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/test/java/no/cantara/jau/util/ClientEnvironmentUtilTest.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau.util; 2 | 3 | import org.testng.annotations.Test; 4 | 5 | import java.util.HashMap; 6 | import java.util.HashSet; 7 | import java.util.Map; 8 | import java.util.Set; 9 | import java.util.SortedMap; 10 | 11 | import static org.testng.Assert.assertEquals; 12 | import static org.testng.Assert.assertNotNull; 13 | import static org.testng.Assert.assertTrue; 14 | 15 | /** 16 | * Created by jorunfa on 11/11/15. 17 | */ 18 | public class ClientEnvironmentUtilTest { 19 | 20 | @Test 21 | public void testGetClientEnvironment() { 22 | SortedMap clientEnvironment = ClientEnvironmentUtil.getClientEnvironment(); 23 | 24 | assertNotNull(clientEnvironment.get("jau.version")); 25 | } 26 | 27 | @Test 28 | public void testGetClientEnvironmentOK() { 29 | SortedMap clientEnvironment = ClientEnvironmentUtil.getClientEnvironment(); 30 | int nicCount = 0; 31 | for (Map.Entry entry : clientEnvironment.entrySet()) { 32 | //System.out.println(entry); 33 | if (entry.getKey().startsWith(ClientEnvironmentUtil.NETWORKINTERFACE)) { 34 | nicCount++; 35 | } 36 | } 37 | assertTrue(nicCount > 0); 38 | } 39 | 40 | @Test 41 | public void testMaskEnvVariables() { 42 | Set variablesToMask = new HashSet<>(); 43 | variablesToMask.add("null-value"); 44 | variablesToMask.add("empty-value"); 45 | variablesToMask.add("length1"); 46 | variablesToMask.add("length4"); 47 | variablesToMask.add("length5"); 48 | variablesToMask.add("length10"); 49 | 50 | Map environmentVariables = new HashMap<>(); 51 | environmentVariables.put("null-value", null); 52 | environmentVariables.put("empty-value", ""); 53 | environmentVariables.put("length1", "1"); 54 | environmentVariables.put("length4", "1234"); 55 | environmentVariables.put("length5", "12345"); 56 | environmentVariables.put("length10", "1234567890"); 57 | environmentVariables.put("unmasked-variable", "unmasked"); 58 | 59 | Map maskedProperties = ClientEnvironmentUtil.maskApplicationEnvProperties(environmentVariables, variablesToMask); 60 | assertEquals(maskedProperties.get("null-value"), null); 61 | assertEquals(maskedProperties.get("empty-value"), ""); 62 | assertEquals(maskedProperties.get("length1"), "1"); 63 | assertEquals(maskedProperties.get("length4"), "1234"); 64 | assertEquals(maskedProperties.get("length5"), "12...45"); 65 | assertEquals(maskedProperties.get("length10"), "12...90"); 66 | assertEquals(maskedProperties.get("unmasked-variable"), "unmasked"); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/test/java/no/cantara/jau/coms/RegisterClientExceptionHandlerTest.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau.coms; 2 | 3 | import com.netflix.hystrix.exception.HystrixRuntimeException; 4 | import org.springframework.util.backoff.BackOffExecution; 5 | import static org.mockito.Mockito.*; 6 | import org.testng.annotations.Test; 7 | 8 | import javax.ws.rs.InternalServerErrorException; 9 | import javax.ws.rs.NotFoundException; 10 | import java.net.ConnectException; 11 | import java.util.concurrent.TimeoutException; 12 | 13 | public class RegisterClientExceptionHandlerTest { 14 | 15 | @Test 16 | public void testShouldHandleConnectExceptionWithRetry() { 17 | BackOffExecution backOffExecution = mock(BackOffExecution.class); 18 | when(backOffExecution.nextBackOff()).thenReturn((long) 1); 19 | 20 | HystrixRuntimeException hystrixRuntimeException = mock(HystrixRuntimeException.class); 21 | when(hystrixRuntimeException.getCause()).thenReturn(new ConnectException()); 22 | 23 | RegisterClientExceptionHandler.handleRegisterClientException(hystrixRuntimeException, null, backOffExecution, ""); 24 | 25 | verify(backOffExecution).nextBackOff(); 26 | } 27 | 28 | @Test 29 | public void testShouldHandleInternalServerErrorExceptionWithRetry() { 30 | BackOffExecution backOffExecution = mock(BackOffExecution.class); 31 | when(backOffExecution.nextBackOff()).thenReturn((long) 1); 32 | 33 | HystrixRuntimeException hystrixRuntimeException = mock(HystrixRuntimeException.class); 34 | when(hystrixRuntimeException.getCause()).thenReturn(new InternalServerErrorException()); 35 | 36 | RegisterClientExceptionHandler.handleRegisterClientException(hystrixRuntimeException, null, backOffExecution, ""); 37 | 38 | verify(backOffExecution).nextBackOff(); 39 | } 40 | 41 | @Test 42 | public void testShouldHandleNotFoundExceptionWithRetry() { 43 | BackOffExecution backOffExecution = mock(BackOffExecution.class); 44 | when(backOffExecution.nextBackOff()).thenReturn((long) 1); 45 | 46 | HystrixRuntimeException hystrixRuntimeException = mock(HystrixRuntimeException.class); 47 | NotFoundException notFoundException = new NotFoundException(); 48 | when(hystrixRuntimeException.getCause()).thenReturn(notFoundException); 49 | 50 | RegisterClientExceptionHandler.handleRegisterClientException(hystrixRuntimeException, null, backOffExecution, ""); 51 | 52 | verify(backOffExecution).nextBackOff(); 53 | } 54 | 55 | @Test 56 | public void testShouldHandleTimeoutExceptionWithRetry() { 57 | BackOffExecution backOffExecution = mock(BackOffExecution.class); 58 | when(backOffExecution.nextBackOff()).thenReturn((long) 1); 59 | 60 | HystrixRuntimeException hystrixRuntimeException = mock(HystrixRuntimeException.class); 61 | when(hystrixRuntimeException.getCause()).thenReturn(new TimeoutException()); 62 | 63 | RegisterClientExceptionHandler.handleRegisterClientException(hystrixRuntimeException, null, backOffExecution, ""); 64 | 65 | verify(backOffExecution).nextBackOff(); 66 | } 67 | 68 | 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/assembly/jdk/download-java.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | :: 4 | :: Default properties. To override these, you can edit download-java-overrides.bat 5 | :: 6 | 7 | :: Download URL for the "normal" 64-bit JDK 8 | SET DOWNLOAD_URL="http://cdn.azul.com/zulu/bin/zulu8.19.0.1-jdk8.0.112-win_x64.zip" 9 | :: Download URL for the 32-bit JDK that will be downloaded on Windows 2003 servers 10 | SET DOWNLOAD_URL_32_BIT= 11 | 12 | :: Download URL for the optional cryptography extension kit, e.g., 13 | :: use parameter extraSecurity to install the optional cryptography extension kit 14 | SET DOWNLOAD_URL_CRYPTO= 15 | IF "%1"=="extraSecurity" ( 16 | SET DOWNLOAD_URL_CRYPTO="http://cdn.azul.com/zcek/bin/ZuluJCEPolicies.zip" 17 | ) 18 | 19 | :: Root directory in the downloaded zip file. Only relevant for the 64-bit JDK. 20 | SET ZIP_ROOT_DIR="zulu8.19.0.1-jdk8.0.112-win_x64" 21 | 22 | :: Set this if using an http proxy. Specified as "host:port". 23 | SET HTTP_PROXY= 24 | 25 | :: Username and password for HTTP authentication. Only relevant for the 32-bit JDK. 26 | SET HTTP_USER= 27 | SET HTTP_PASSWORD= 28 | 29 | 30 | SET ZIP_FILE="java.zip" 31 | SET ZIP_FILE_CRYPTO="crypto.zip" 32 | 33 | CALL config_override\download-java-overrides.bat 34 | 35 | :: Windows Server 2003 needs 32-bit Java 36 | ver | findstr /i "5\.2\." > nul 37 | IF %ERRORLEVEL% == 0 ( 38 | GOTO use32bit 39 | ) ELSE ( 40 | GOTO normal 41 | ) 42 | 43 | 44 | 45 | :use32bit 46 | echo Downloading Java from %DOWNLOAD_URL_32_BIT% 47 | 48 | IF defined HTTP_PROXY ( 49 | wget.exe --user=%HTTP_USER% --password=%HTTP_PASSWORD% -e use_proxy=yes -e http_proxy=%HTTP_PROXY% -O %ZIP_FILE% --content-disposition %DOWNLOAD_URL_32_BIT% 50 | ) ELSE ( 51 | wget.exe --user=%HTTP_USER% --password=%HTTP_PASSWORD% -O %ZIP_FILE% --content-disposition %DOWNLOAD_URL_32_BIT% 52 | ) 53 | 54 | echo Unzipping %ZIP_FILE% 55 | unzip -q -o %ZIP_FILE% -d java 56 | if exist java\jre1.8.0_40 ( 57 | echo Copying java files to java\bin 58 | move java\jre1.8.0_40\*.* java\ 59 | move java\jre1.8.0_40\bin java\bin 60 | move java\jre1.8.0_40\lib java\lib 61 | xcopy /E /O /Y java\bin\client\* java\bin\server\ 62 | ) 63 | 64 | GOTO crypto 65 | 66 | 67 | 68 | :normal 69 | echo Downloading Java from %DOWNLOAD_URL% 70 | 71 | IF defined HTTP_PROXY ( 72 | wget.exe -e use_proxy=yes -e http_proxy=%HTTP_PROXY% -O %ZIP_FILE% %DOWNLOAD_URL% 73 | ) ELSE ( 74 | wget.exe -O %ZIP_FILE% %DOWNLOAD_URL% 75 | ) 76 | 77 | echo Unzipping %ZIP_FILE% 78 | unzip.exe -q -o %ZIP_FILE% 79 | move %ZIP_ROOT_DIR% java 80 | 81 | GOTO crypto 82 | 83 | 84 | 85 | :crypto 86 | IF defined DOWNLOAD_URL_CRYPTO ( 87 | echo Downloading crypto extension from %DOWNLOAD_URL_CRYPTO% 88 | IF defined HTTP_PROXY ( 89 | wget.exe -e use_proxy=yes -e http_proxy=%HTTP_PROXY% -O %ZIP_FILE_CRYPTO% %DOWNLOAD_URL_CRYPTO% 90 | ) ELSE ( 91 | wget.exe -O %ZIP_FILE_CRYPTO% %DOWNLOAD_URL_CRYPTO% 92 | ) 93 | echo Unzipping %ZIP_FILE_CRYPTO% 94 | unzip.exe -o -j %ZIP_FILE_CRYPTO% *.jar -d java/jre/lib/security 95 | ) 96 | 97 | GOTO finish 98 | 99 | 100 | 101 | 102 | :finish 103 | 104 | :: Copy windows ntlmauth dll 105 | if exist java\bin ( 106 | echo Copying ntlmauth.dll to java\bin 107 | copy ntlmauth.dll java\bin 108 | ) 109 | 110 | if exist %ZIP_FILE% ( 111 | del %ZIP_FILE% 112 | ) 113 | if exist %ZIP_FILE_CRYPTO% ( 114 | del %ZIP_FILE_CRYPTO% 115 | ) 116 | -------------------------------------------------------------------------------- /src/main/java/no/cantara/jau/Main.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau; 2 | 3 | import no.cantara.cs.client.ConfigServiceClient; 4 | import no.cantara.jau.coms.RegisterClientHelper; 5 | import no.cantara.jau.duplicatehandler.DuplicateProcessHandler; 6 | import no.cantara.jau.duplicatehandler.LastRunningProcessFileUtil; 7 | import no.cantara.jau.duplicatehandler.ProcessExecutorFetcher; 8 | import no.cantara.jau.eventextraction.EventExtractorService; 9 | import no.cantara.jau.eventextraction.EventRepo; 10 | import no.cantara.jau.util.AppConfig; 11 | import no.cantara.jau.util.DisableSSLVerification; 12 | import no.cantara.jau.util.PropertiesHelper; 13 | import no.cantara.jau.util.ProxyFixer; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | import java.io.File; 18 | import java.util.Arrays; 19 | import java.util.List; 20 | 21 | /** 22 | * @author Erik Drolshammer 2015-07-13. 23 | */ 24 | public class Main { 25 | 26 | private static final Logger log = LoggerFactory.getLogger(Main.class); 27 | 28 | public static void main(String[] args) { 29 | AppConfig.init("jau"); 30 | ProxyFixer.fixProxy(PropertiesHelper.getPropertiesFromConfigFile(PropertiesHelper.JAU_CONFIG_FILENAME)); 31 | 32 | String configServiceUrl = PropertiesHelper.getConfigServiceUrl(); 33 | 34 | if (configServiceUrl == null) { 35 | log.error("Application cannot start! {} not set in {}.", 36 | PropertiesHelper.CONFIG_SERVICE_URL_KEY, PropertiesHelper.JAU_CONFIG_FILENAME); 37 | System.exit(1); 38 | } 39 | 40 | String clientName = PropertiesHelper.getClientName(); 41 | String username = PropertiesHelper.getUsername(); 42 | String password = PropertiesHelper.getPassword(); 43 | String artifactId = PropertiesHelper.getArtifactId(); 44 | String clientId = PropertiesHelper.getClientId(); 45 | String startPattern = PropertiesHelper.getStartPattern(); 46 | 47 | log.debug("Resolved clientId={}", clientId); 48 | 49 | int updateInterval = PropertiesHelper.getUpdateInterval(); 50 | int isRunningInterval = PropertiesHelper.getIsRunningInterval(); 51 | 52 | String domains = PropertiesHelper.getStringProperty(null, "disable.tlscheck.domains", ""); 53 | if (!"".equals(domains)) { 54 | List domainsToIgnoreTlsCheck = Arrays.asList(domains.split(",")); 55 | DisableSSLVerification.disableForDomains(domainsToIgnoreTlsCheck); 56 | } 57 | 58 | ConfigServiceClient configServiceClient = new ConfigServiceClient(configServiceUrl, username, password); 59 | RegisterClientHelper registerClientHelper = new RegisterClientHelper(configServiceClient, artifactId, clientName, clientId); 60 | 61 | ProcessExecutorFetcher processExecutorFetcher = new ProcessExecutorFetcher(); 62 | LastRunningProcessFileUtil fileUtil = new LastRunningProcessFileUtil( 63 | DuplicateProcessHandler.RUNNING_PROCESS_FILENAME); 64 | DuplicateProcessHandler duplicateProcessHandler = new DuplicateProcessHandler(processExecutorFetcher, fileUtil); 65 | 66 | String workingDirectory = "./"; 67 | ApplicationProcess processHolder = new ApplicationProcess(duplicateProcessHandler); 68 | processHolder.setWorkingDirectory(new File(workingDirectory)); 69 | 70 | EventExtractorService extractorService = new EventExtractorService(new EventRepo(), startPattern); 71 | 72 | new JavaAutoUpdater(configServiceClient, registerClientHelper, processHolder, duplicateProcessHandler, 73 | extractorService) 74 | .start(updateInterval, isRunningInterval); 75 | } 76 | } -------------------------------------------------------------------------------- /src/main/java/org/springframework/util/backoff/FixedBackOff.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002-2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.util.backoff; 18 | 19 | /** 20 | * A simple {@link BackOff} implementation that provides a fixed interval 21 | * between two attempts and a maximum number of retries. 22 | * 23 | * @author Stephane Nicoll 24 | * @since 4.1 25 | */ 26 | public class FixedBackOff implements BackOff { 27 | 28 | /** 29 | * The default recovery interval: 5000 ms = 5 seconds. 30 | */ 31 | public static final long DEFAULT_INTERVAL = 5000; 32 | 33 | /** 34 | * Constant value indicating an unlimited number of attempts. 35 | */ 36 | public static final long UNLIMITED_ATTEMPTS = Long.MAX_VALUE; 37 | 38 | private long interval = DEFAULT_INTERVAL; 39 | 40 | private long maxAttempts = UNLIMITED_ATTEMPTS; 41 | 42 | 43 | /** 44 | * Create an instance with an interval of {@value #DEFAULT_INTERVAL} 45 | * ms and an unlimited number of attempts. 46 | */ 47 | public FixedBackOff() { 48 | } 49 | 50 | /** 51 | * Create an instance. 52 | * @param interval the interval between two attempts 53 | * @param maxAttempts the maximum number of attempts 54 | */ 55 | public FixedBackOff(long interval, long maxAttempts) { 56 | this.interval = interval; 57 | this.maxAttempts = maxAttempts; 58 | } 59 | 60 | /** 61 | * Set the interval between two attempts in milliseconds. 62 | */ 63 | public void setInterval(long interval) { 64 | this.interval = interval; 65 | } 66 | 67 | /** 68 | * Return the interval between two attempts in milliseconds. 69 | */ 70 | public long getInterval() { 71 | return interval; 72 | } 73 | 74 | /** 75 | * Set the maximum number of attempts in milliseconds. 76 | */ 77 | public void setMaxAttempts(long maxAttempts) { 78 | this.maxAttempts = maxAttempts; 79 | } 80 | 81 | /** 82 | * Return the maximum number of attempts in milliseconds. 83 | */ 84 | public long getMaxAttempts() { 85 | return maxAttempts; 86 | } 87 | 88 | @Override 89 | public BackOffExecution start() { 90 | return new FixedBackOffExecution(); 91 | } 92 | 93 | 94 | private class FixedBackOffExecution implements BackOffExecution { 95 | 96 | private long currentAttempts = 0; 97 | 98 | @Override 99 | public long nextBackOff() { 100 | this.currentAttempts++; 101 | if (this.currentAttempts <= getMaxAttempts()) { 102 | return getInterval(); 103 | } 104 | else { 105 | return STOP; 106 | } 107 | } 108 | 109 | @Override 110 | public String toString() { 111 | final StringBuilder sb = new StringBuilder("FixedBackOff{"); 112 | sb.append("interval=").append(FixedBackOff.this.interval); 113 | String attemptValue = (FixedBackOff.this.maxAttempts == Long.MAX_VALUE ? "unlimited" 114 | : String.valueOf(FixedBackOff.this.maxAttempts)); 115 | sb.append(", currentAttempts=").append(this.currentAttempts); 116 | sb.append(", maxAttempts=").append(attemptValue); 117 | sb.append('}'); 118 | return sb.toString(); 119 | } 120 | 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /src/test/java/no/cantara/jau/ApplicationProcessTest.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau; 2 | 3 | import no.cantara.jau.duplicatehandler.DuplicateProcessHandler; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.testng.annotations.AfterClass; 7 | import org.testng.annotations.BeforeClass; 8 | import org.testng.annotations.Test; 9 | 10 | import java.io.File; 11 | import java.util.concurrent.Executors; 12 | import java.util.concurrent.ScheduledExecutorService; 13 | import java.util.concurrent.ScheduledFuture; 14 | 15 | import static java.util.concurrent.TimeUnit.MILLISECONDS; 16 | import static org.mockito.Mockito.mock; 17 | import static org.testng.Assert.assertFalse; 18 | import static org.testng.Assert.assertTrue; 19 | 20 | /** 21 | * Created by totto on 13.09.15. 22 | */ 23 | public class ApplicationProcessTest { 24 | 25 | private ApplicationProcess processHolder; 26 | private static final Logger log = LoggerFactory.getLogger(ApplicationProcessTest.class); 27 | private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); 28 | 29 | 30 | 31 | @BeforeClass 32 | public void startServer() throws InterruptedException { 33 | //Mock out duplicate process handler. Not the point of the test 34 | DuplicateProcessHandler duplicateProcessHandler = mock(DuplicateProcessHandler.class); 35 | processHolder = new ApplicationProcess(duplicateProcessHandler); 36 | processHolder.setWorkingDirectory(new File("./")); 37 | String[] command = new String[2]; 38 | command[0] = "sleep"; 39 | command[1] = "4"; 40 | int updateInterval=7; 41 | processHolder.setCommand(command); 42 | processHolder.startProcess(); 43 | 44 | final ScheduledFuture restarterHandle = scheduler.scheduleAtFixedRate( 45 | () -> { 46 | 47 | try { 48 | // Restart, whatever the reason the process is not running. 49 | if (!processHolder.processIsRunning()) { 50 | log.info("Process is not running - restarting... clientId={}, lastChanged={}, command={}", 51 | processHolder.getClientId(), processHolder.getLastChangedTimestamp(), processHolder.getCommand()); 52 | processHolder.startProcess(); 53 | } 54 | } catch (Exception e) { 55 | log.warn("Error thrown from scheduled lambda.", e); 56 | } 57 | }, 58 | 1, updateInterval, MILLISECONDS 59 | ); 60 | 61 | } 62 | 63 | @AfterClass 64 | public void stop() { 65 | if (processHolder!=null){ 66 | processHolder.stopProcess(); 67 | } 68 | } 69 | 70 | 71 | @Test 72 | public void testProcessRunning() throws Exception { 73 | Thread.sleep(1232); 74 | assertTrue(processHolder.processIsRunning(), "First check"); 75 | Thread.sleep(1232); 76 | assertTrue(processHolder.processIsRunning(), "Second check"); 77 | Thread.sleep(1232); 78 | assertTrue(processHolder.processIsRunning(), "Third check"); 79 | Thread.sleep(2232); 80 | assertTrue(processHolder.processIsRunning(), "Fourth check"); 81 | processHolder.stopProcess(); 82 | assertFalse(processHolder.processIsRunning(), "Fifth check"); 83 | Thread.sleep(1331); 84 | assertTrue(processHolder.processIsRunning(), "Sixt check"); 85 | processHolder.stopProcess(); 86 | assertFalse(processHolder.processIsRunning(), "Seventh check"); 87 | Thread.sleep(3223); 88 | assertTrue(processHolder.processIsRunning(), "Eigth check"); 89 | 90 | 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/no/cantara/jau/ApplicationProcess.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau; 2 | 3 | import no.cantara.jau.duplicatehandler.DuplicateProcessHandler; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import java.io.File; 8 | import java.io.IOException; 9 | import java.util.Map; 10 | 11 | /** 12 | * Wrapper of process and related data, 13 | * primarily used to circumvent Java 8's "final" restriction on closures. 14 | */ 15 | public class ApplicationProcess { 16 | private static final Logger log = LoggerFactory.getLogger(ApplicationProcess.class); 17 | private File workingDirectory; 18 | private String[] command; 19 | private Map environment; 20 | private Process runningProcess; 21 | 22 | private String clientId; 23 | private String lastChangedTimestamp; 24 | private DuplicateProcessHandler duplicateProcessHandler; 25 | 26 | public ApplicationProcess(DuplicateProcessHandler duplicateProcessHandler) { 27 | this.duplicateProcessHandler = duplicateProcessHandler; 28 | } 29 | 30 | public boolean processIsRunning() { 31 | return runningProcess != null && runningProcess.isAlive(); 32 | } 33 | 34 | public void startProcess() { 35 | ProcessBuilder pb = new ProcessBuilder(command).inheritIO().directory(workingDirectory); 36 | if (environment != null) { 37 | pb.environment().putAll(environment); 38 | } 39 | 40 | try { 41 | runningProcess = pb.start(); 42 | Thread.sleep(1000); // Gives the process time to fail 43 | if(runningProcess.isAlive()) { 44 | duplicateProcessHandler.findRunningManagedProcessPidAndWriteToFile(runningProcess); 45 | } else { 46 | log.warn("Failed to start process with command: {}, workingDirectory: {}, clientId: {}, lastChangedTimestamp: {}", command, workingDirectory, clientId, lastChangedTimestamp); 47 | } 48 | } catch (IOException e) { 49 | throw new RuntimeException("IOException while trying to start process with command '" + String.join(" ", command) + "' from directory '" + workingDirectory + "'.", e); 50 | } catch (InterruptedException e) { 51 | log.warn("Not allowed to sleep for 1 second", e); 52 | } 53 | } 54 | 55 | public void stopProcess() { 56 | log.debug("Destroying running process"); 57 | if (!processIsRunning()) { 58 | log.debug("Tried to stop process, but no process was running."); 59 | return; 60 | } 61 | runningProcess.destroy(); 62 | try { 63 | runningProcess.waitFor(); 64 | log.debug("Successfully destroyed running process"); 65 | } catch (InterruptedException e) { 66 | log.debug("Interrupted while waiting for process to shut down.", e); 67 | } 68 | } 69 | 70 | public void setWorkingDirectory(File workingDirectory) { 71 | this.workingDirectory = workingDirectory; 72 | } 73 | public void setCommand(String[] command) { 74 | this.command = command; 75 | } 76 | public void setEnvironment(Map environment) { 77 | this.environment = environment; 78 | } 79 | public void setClientId(String clientId) { 80 | this.clientId = clientId; 81 | } 82 | public void setLastChangedTimestamp(String lastChangedTimestamp) { 83 | this.lastChangedTimestamp = lastChangedTimestamp; 84 | } 85 | 86 | public File getWorkingDirectory() { 87 | return workingDirectory; 88 | } 89 | public String[] getCommand() { 90 | return command; 91 | } 92 | public String getClientId() { 93 | return clientId; 94 | } 95 | public String getLastChangedTimestamp() { 96 | return lastChangedTimestamp; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/no/cantara/jau/util/ClientEnvironmentUtil.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau.util; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.net.InetAddress; 7 | import java.net.InterfaceAddress; 8 | import java.net.NetworkInterface; 9 | import java.net.SocketException; 10 | import java.util.*; 11 | import java.util.Map.Entry; 12 | 13 | /** 14 | * @author Erik Drolshammer 2015-09-20. 15 | */ 16 | public class ClientEnvironmentUtil { 17 | private static final Logger log = LoggerFactory.getLogger(ClientEnvironmentUtil.class); 18 | public static final String NETWORKINTERFACE = "networkinterface_"; 19 | 20 | public static SortedMap getClientEnvironment(Properties applicationState, String processIsRunning) { 21 | SortedMap clientEnv = new TreeMap<>(); 22 | 23 | try { 24 | Enumeration nics = NetworkInterface.getNetworkInterfaces(); 25 | while (nics.hasMoreElements()) { 26 | NetworkInterface nic = nics.nextElement(); 27 | for (InterfaceAddress interfaceAddress : nic.getInterfaceAddresses()) { 28 | InetAddress address = interfaceAddress.getAddress(); 29 | if (address.isLoopbackAddress()) { 30 | continue; 31 | } 32 | 33 | if (address.isSiteLocalAddress()) { 34 | clientEnv.put(NETWORKINTERFACE + nic.getName(), address.getHostAddress()); 35 | } 36 | } 37 | } 38 | } catch (SocketException e) { 39 | log.warn("getNetworkInterfaces failed. Networkinterface addresses will not be availble."); 40 | } 41 | 42 | // We mask environment variables that are defined in appliction-env.properties, the assumption is that these are 43 | // sensitive and we do want to send them as heartbeat data 44 | Set propertiesToMask = PropertiesHelper.getPropertiesFromConfigFile(PropertiesHelper.APPLICATION_ENV_FILENAME).stringPropertyNames(); 45 | clientEnv.putAll(maskApplicationEnvProperties(System.getenv(), propertiesToMask)); 46 | Map mapProperties = new HashMap(); 47 | Properties systemProperties = System.getProperties(); 48 | for(Entry x : systemProperties.entrySet()) { 49 | mapProperties.put((String)x.getKey(), (String)x.getValue()); 50 | } 51 | clientEnv.putAll(maskApplicationEnvProperties(mapProperties, propertiesToMask)); 52 | String version = PropertiesHelper.getVersion(); 53 | clientEnv.put("jau.version", version); 54 | clientEnv.put("applicationState", String.valueOf(applicationState)); 55 | clientEnv.put("processIsRunning", processIsRunning); 56 | clientEnv.put("processIsRunning timestamp", new Date().toString()); 57 | return clientEnv; 58 | } 59 | 60 | public static SortedMap getClientEnvironment() { 61 | return getClientEnvironment(new Properties(), "information not available"); 62 | } 63 | 64 | static Map maskApplicationEnvProperties(Map environmentVariables, Set variablesToMask) { 65 | HashMap masked = new HashMap<>(); 66 | for (String variableKey : environmentVariables.keySet()) { 67 | if (variablesToMask.contains(variableKey)) { 68 | masked.put(variableKey, maskEnvironmentVariable(environmentVariables.get(variableKey))); 69 | } else { 70 | masked.put(variableKey, environmentVariables.get(variableKey)); 71 | } 72 | } 73 | return masked; 74 | } 75 | 76 | private static String maskEnvironmentVariable(String variableValue) { 77 | if (variableValue == null || variableValue.length() < 5) { 78 | // We assume short variable values are not sensitive 79 | return variableValue; 80 | } 81 | return variableValue.substring(0, 2) + "..." + variableValue.substring(variableValue.length() - 2, variableValue.length()); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/test/java/no/cantara/jau/eventextraction/EventExtractorServiceTest.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau.eventextraction; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import no.cantara.cs.client.EventExtractionUtil; 5 | import no.cantara.cs.dto.event.*; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.testng.Assert; 9 | import org.testng.annotations.Test; 10 | 11 | import java.nio.file.Files; 12 | import java.nio.file.Paths; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | import java.util.Map; 16 | import java.util.stream.Collectors; 17 | 18 | import static org.testng.Assert.assertEquals; 19 | import static org.testng.Assert.assertNotEquals; 20 | 21 | public class EventExtractorServiceTest { 22 | private static final Logger log = LoggerFactory.getLogger(EventExtractorServiceTest.class); 23 | 24 | @Test 25 | public void shouldGroupTagsByFileName() { 26 | EventExtractionConfig config = new EventExtractionConfig("jau"); 27 | config.addEventExtractionTag(new EventExtractionTag("This a is a tag", "\\btest\\b", "path/to/log.log")); 28 | config.addEventExtractionTag(new EventExtractionTag("Seconds tag", "\\blalala\\b", "path/to/log.log")); 29 | 30 | Map> mapped = EventExtractionUtil.groupExtractionConfigsByFile(config); 31 | 32 | assertEquals(mapped.size(), 1); 33 | Assert.assertNotNull(mapped.get("path/to/log.log")); 34 | } 35 | 36 | @Test 37 | public void shouldExtractEventsFromFiles() throws Exception { 38 | EventRepo repo = new EventRepo(); 39 | EventExtractorService service = new EventExtractorService(repo, null); 40 | EventExtractionConfig config = new EventExtractionConfig("jau"); 41 | String filePath1 = getLogFile("jau-test-log.logg"); 42 | String filePath2 = getLogFile("ma-test-log.logg"); 43 | config.addEventExtractionTag(new EventExtractionTag("This a is a tag", "\\btest\\b", 44 | filePath1)); 45 | config.addEventExtractionTag(new EventExtractionTag("MDC tag test", "\\bmdc-tag-test\\b", 46 | filePath2)); 47 | List configs = new ArrayList<>(); 48 | configs.add(config); 49 | service.updateConfigs(configs); 50 | List events = service.extractEvents(); 51 | 52 | assertNotEquals(events.size(), 0); 53 | 54 | ExtractedEventsStore mappedEvents = EventExtractionUtil.mapToExtractedEvents(events); 55 | ObjectMapper mapper = new ObjectMapper(); 56 | log.info(mapper.writeValueAsString(mappedEvents)); 57 | 58 | EventTag tag = mappedEvents.getEventGroup("jau").getEventFile(filePath1) 59 | .getEventTag("This a is a tag"); 60 | List manuallyCollected = events.stream() 61 | .filter(e -> e.getTag().equals("This a is a tag")) 62 | .collect(Collectors.toList()); 63 | 64 | assertEquals(tag.getEvents().size(), manuallyCollected.size()); 65 | } 66 | 67 | @Test 68 | public void shouldExtractEventsFromMultipleLargeFiles() throws Exception { 69 | EventRepo repo = new EventRepo(); 70 | EventExtractorService service = new EventExtractorService(repo, null); 71 | EventExtractionConfig config = new EventExtractionConfig("jau"); 72 | String filePath1 = getLogFile("mymanagedapplication-2016-01-07.logg"); 73 | String filePath2 = getLogFile("jau-2016-01-10.logg"); 74 | config.addEventExtractionTag(new EventExtractionTag("This a is a tag", "\\b200\\b", 75 | filePath1)); 76 | config.addEventExtractionTag(new EventExtractionTag("MDC tag test", "\\bmdc-tag-test\\b", 77 | filePath2)); 78 | List configs = new ArrayList<>(); 79 | configs.add(config); 80 | service.updateConfigs(configs); 81 | 82 | List events = service.extractEvents(); 83 | 84 | assertNotEquals(events.size(), 0); 85 | } 86 | 87 | private String getLogFile(String name) throws Exception { 88 | Files.deleteIfExists(Paths.get(name + ".properties")); 89 | return ClassLoader.getSystemResource(name).toURI().getPath(); 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/no/cantara/jau/eventextraction/EventExtractor.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau.eventextraction; 2 | 3 | import no.cantara.cs.dto.event.EventExtractionTag; 4 | import no.cantara.jau.util.PropertiesHelper; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.io.File; 10 | import java.util.Date; 11 | import java.util.List; 12 | import java.util.Properties; 13 | import java.util.concurrent.Callable; 14 | 15 | public class EventExtractor implements Callable { 16 | private static final Logger LOG = LoggerFactory.getLogger(EventExtractor.class); 17 | private static final String KEY_LAST_MODIFIED = "lastModified"; 18 | private static final String KEY_LAST_LINE_READ = "lastLineRead"; 19 | private static final String KEY_LAST_FILE_SIZE = "lastFileSize"; 20 | 21 | private final List extractionTags; 22 | private final String managedProcessLogFilePath; 23 | private final File managedProcessLogFile; 24 | private final EventRepo eventRepo; 25 | private final String groupName; 26 | private final String startPattern; 27 | private long lastLineRead; 28 | private long lastModified; 29 | private long lastFileSize; 30 | private Properties properties; 31 | 32 | public EventExtractor(EventRepo eventRepo, List extractionTags, 33 | String managedProcessLogFilePath, String groupName, String startPattern) { 34 | this.eventRepo = eventRepo; 35 | this.groupName = groupName; 36 | this.extractionTags = extractionTags; 37 | this.startPattern = startPattern; 38 | this.managedProcessLogFilePath = managedProcessLogFilePath; 39 | managedProcessLogFile = new File(managedProcessLogFilePath); 40 | 41 | loadState(); 42 | } 43 | 44 | private void loadState() { 45 | properties = PropertiesHelper.loadProperties(getPropertyFile()); 46 | lastModified = PropertiesHelper.getLongProperty(properties, KEY_LAST_MODIFIED, 0L); 47 | lastLineRead = PropertiesHelper.getLongProperty(properties, KEY_LAST_LINE_READ, 0L); 48 | lastFileSize = PropertiesHelper.getLongProperty(properties, KEY_LAST_FILE_SIZE, 0L); 49 | } 50 | 51 | private void saveState() { 52 | PropertiesHelper.setLongProperty(properties, KEY_LAST_MODIFIED, lastModified); 53 | PropertiesHelper.setLongProperty(properties, KEY_LAST_LINE_READ, lastLineRead); 54 | PropertiesHelper.setLongProperty(properties, KEY_LAST_FILE_SIZE, lastFileSize); 55 | PropertiesHelper.saveProperties(properties, getPropertyFile()); 56 | } 57 | 58 | private File getPropertyFile() { 59 | return new File(managedProcessLogFile.getName() + ".properties"); 60 | } 61 | 62 | private void checkForEvents() { 63 | if (fileHasBeenModified()) { 64 | try { 65 | lastLineRead = new CommandExtractEventsFromFile(eventRepo, lastLineRead, 66 | managedProcessLogFilePath, groupName, startPattern, extractionTags).run(); 67 | saveState(); 68 | } catch (Exception e) { 69 | LOG.error("Failed to extract events from file {}", managedProcessLogFilePath, e); 70 | } 71 | } 72 | } 73 | 74 | private boolean fileHasBeenModified() { 75 | if (managedProcessLogFile.lastModified() > lastModified || managedProcessLogFile.length() != lastFileSize) { 76 | LOG.trace("{} is modified since last extraction. Extracting...", managedProcessLogFilePath); 77 | lastModified = managedProcessLogFile.lastModified(); 78 | 79 | // If file has shrunk (due to rolling log files), start reading from the first line. 80 | if (managedProcessLogFile.length() < lastFileSize) { 81 | LOG.info("{} has shrunk since last extraction. Starting from first line.", managedProcessLogFilePath); 82 | lastLineRead = 0; 83 | } 84 | lastFileSize = managedProcessLogFile.length(); 85 | return true; 86 | } 87 | LOG.trace("{} has NOT been modified since last extraction. Will NOT extract. Last extracted at {}. File last-modified now is {}", 88 | managedProcessLogFilePath, new Date(lastModified), new Date(managedProcessLogFile.lastModified())); 89 | return false; 90 | } 91 | 92 | @Override 93 | public String call() throws Exception { 94 | checkForEvents(); 95 | return null; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Java-Auto-Update 2 | Java Auto-Update - wrapper to automatically upgrade a Java application. 3 | 4 | ![GitHub tag (latest SemVer)](https://img.shields.io/github/v/tag/Cantara/Java-Auto-Update) 5 | ![Build Status](https://jenkins.quadim.ai/buildStatus/icon?job=Java-Auto-Update) [![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](http://www.repostatus.org/badges/latest/active.svg)](http://www.repostatus.org/#active) [![Known Vulnerabilities](https://snyk.io/test/github/Cantara/Java-Auto-Update/badge.svg)](https://snyk.io/test/github/Cantara/Java-Auto-Update) 6 | 7 | ## Install on Windows 8 | 9 | 1. Download zip file from https://mvnrepo.cantara.no/content/repositories/releases/no/cantara/jau/java-auto-update/0.11.1/java-auto-update-0.11.1.zip 10 | 2. unzip 11 | 3. Update configServiceUrl in config_override/jau.properties 12 | 4. Execute download-java.bat. Use the parameter extraSecurity if you want to add the optional cryptography extension kit 13 | 5. Open command prompt with 'Run as Administrator' 14 | 6. cd java-auto-update-0.10.5-SNAPSHOT\bin 15 | 7. java-auto-update remove & java-auto-update install 16 | 8. java-auto-update start 17 | 18 | Tip! If it fails to start, check the wrapper.log. 19 | 20 | 21 | ## Run on Linux 22 | 23 | 1. wget https://mvnrepo.cantara.no/content/repositories/releases/no/cantara/jau/java-auto-update/0.11.1/java-auto-update-0.11.1.zip 24 | 2. java -Dconfigservice.url=http://localhost:7000/jau/serviceconfig/query?clientid=clientid1 -jar java-auto-update-0.10.5.jar 25 | 26 | 27 | ## Configuration 28 | 29 | 30 | If JAU is running behind a proxy, use the following properties 31 | * "http.useProxy" 32 | * "http.proxyPort" 33 | * "http.proxyHost" 34 | * "https.proxyHost" 35 | * "https.proxyPort" 36 | 37 | If JAU is running behind a proxy that performs SSL sniffing, you can use the 38 | following property to ignore certificate validation for given domains. Matches 39 | the Subject DN of the certificate, so if a wildcard certificate is used, specify 40 | like "*.example.com". Separate multiple domains using a comma. 41 | 42 | - `disable.tlscheck.domains`, e.g. 43 | `disable.tlscheck.domains="*.example.com,subdomain.domain.com"` 44 | 45 | jau.properties 46 | ``` 47 | configservice.url=http://localhost:8086/jau/client 48 | configservice.username=read 49 | configservice.password=baretillesing 50 | configservice.artifactid=cantara-demo 51 | 52 | updateinterval=60 53 | isrunninginterval=40 54 | 55 | clientName=local-jau 56 | 57 | monitor.events=testkey 58 | 59 | # "startPattern" is a regex defining the start of a log entry. Setting this property causes multi-line 60 | # log entries to be collated before being sent to ConfigService. 61 | # For example, if your log entries starts with a UTC timestamp (e.g., 2016-06-09T14:01:05.348) you can 62 | # use the following regex: 63 | 64 | # startPattern=\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3} 65 | ``` 66 | 67 | 68 | ## Example application configuration JAU receives from ConfigService 69 | 70 | ``` 71 | { 72 | "name": "hello-world_0.1-SNAPSHOT", 73 | "lastChanged": "2016-03-09T07:50:18.994Z", 74 | "downloadItems": [ 75 | { 76 | "url": "repository-url/hello-world-0.1-SNAPSHOT.jar", 77 | "username": "basic-auth-username", 78 | "password": "basic-auth-password", 79 | "metadata": { 80 | "groupId": "com.example", 81 | "artifactId": "hello-world-service", 82 | "version": "0.1-SNAPSHOT", 83 | "packaging": "jar", 84 | "lastUpdated": null, 85 | "buildNumber": null 86 | } 87 | } 88 | ], 89 | "configurationStores": [ 90 | { 91 | "fileName": "helloworld_overrides.properties", 92 | "properties": { 93 | "hello.world.message": "Hello World" 94 | } 95 | } 96 | ], 97 | "eventExtractionConfigs" : [ { 98 | "groupName" : "hw-agent", 99 | "tags" : [ { 100 | "tagName" : "jau", 101 | "regex" : ".*", 102 | "filePath" : "logs/jau.log" 103 | }, { 104 | "tagName" : "agent", 105 | "regex" : ".*", 106 | "filePath" : "logs/hwagent.log" 107 | } ] 108 | } ], 109 | 110 | ], 111 | "startServiceScript": "java -jar hello-world-0.1-SNAPSHOT.jar" 112 | } 113 | ``` 114 | 115 | ## Develop 116 | 117 | Java Auto-Update depends on ConfigService, so this must be available in your maven repositories. 118 | You can download ConfigService from github: https://github.com/Cantara/ConfigService 119 | 120 | -------------------------------------------------------------------------------- /src/test/java/no/cantara/jau/coms/CommandRegisterClientTest.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau.coms; 2 | 3 | import com.netflix.hystrix.exception.HystrixRuntimeException; 4 | import no.cantara.cs.client.ConfigServiceClient; 5 | import no.cantara.cs.dto.ClientRegistrationRequest; 6 | import org.testng.annotations.Test; 7 | 8 | import javax.ws.rs.BadRequestException; 9 | import javax.ws.rs.InternalServerErrorException; 10 | import javax.ws.rs.NotFoundException; 11 | import javax.ws.rs.core.NoContentException; 12 | import java.io.IOException; 13 | 14 | import static org.mockito.ArgumentMatchers.any; 15 | import static org.mockito.Mockito.mock; 16 | import static org.mockito.Mockito.when; 17 | import static org.testng.Assert.assertTrue; 18 | import static org.testng.Assert.fail; 19 | 20 | /** 21 | * Created by jorunfa on 28/10/15. 22 | */ 23 | public class CommandRegisterClientTest { 24 | 25 | @Test 26 | public void test404ShouldThrowHystrixRuntimeException() throws IOException { 27 | ConfigServiceClient configServiceClient = mock(ConfigServiceClient.class); 28 | 29 | when(configServiceClient.registerClient(any(ClientRegistrationRequest.class))).thenThrow(NotFoundException.class); 30 | 31 | try { 32 | new CommandRegisterClient("", configServiceClient, "", "").execute(); 33 | fail("Should've gotten an exception"); 34 | } catch (HystrixRuntimeException e) { 35 | Throwable cause = e.getCause(); 36 | assertTrue(cause instanceof NotFoundException); 37 | } catch (Exception e) { 38 | fail("Should not get another exception."); 39 | } 40 | } 41 | 42 | @Test 43 | public void testInternalServerErrorShouldThrowHystrixRuntimeException() throws IOException { 44 | ConfigServiceClient configServiceClient = mock(ConfigServiceClient.class); 45 | 46 | when(configServiceClient.registerClient(any(ClientRegistrationRequest.class))).thenThrow(InternalServerErrorException.class); 47 | 48 | try { 49 | new CommandRegisterClient("", configServiceClient, "", "").execute(); 50 | fail("Should've gotten an exception"); 51 | } catch (HystrixRuntimeException e) { 52 | Throwable cause = e.getCause(); 53 | assertTrue(cause instanceof InternalServerErrorException); 54 | } catch (Exception e) { 55 | fail("Should not get another exception."); 56 | } 57 | } 58 | 59 | @Test 60 | public void testBadRequestExceptionShouldThrowHystrixRuntimeException() throws IOException { 61 | ConfigServiceClient configServiceClient = mock(ConfigServiceClient.class); 62 | 63 | when(configServiceClient.registerClient(any(ClientRegistrationRequest.class))).thenThrow(BadRequestException.class); 64 | 65 | try { 66 | new CommandRegisterClient("", configServiceClient, "", "").execute(); 67 | fail("Should've gotten an exception"); 68 | } catch (HystrixRuntimeException e) { 69 | Throwable cause = e.getCause(); 70 | assertTrue(cause instanceof BadRequestException); 71 | } catch (Exception e) { 72 | fail("Should not get another exception."); 73 | } 74 | } 75 | 76 | @Test 77 | public void testNoContentExceptionShouldThrowHystrixRuntimeException() throws IOException { 78 | ConfigServiceClient configServiceClient = mock(ConfigServiceClient.class); 79 | 80 | when(configServiceClient.registerClient(any(ClientRegistrationRequest.class))).thenThrow(NoContentException.class); 81 | 82 | try { 83 | new CommandRegisterClient("", configServiceClient, "", "").execute(); 84 | fail("Should've gotten an exception"); 85 | } catch (HystrixRuntimeException e) { 86 | Throwable cause = e.getCause(); 87 | assertTrue(cause instanceof NoContentException); 88 | } catch (Exception e) { 89 | fail("Should not get another exception."); 90 | } 91 | } 92 | 93 | 94 | @Test 95 | public void testIllegalStateExceptionShouldThrowHystrixRuntimeException() throws IOException { 96 | ConfigServiceClient configServiceClient = mock(ConfigServiceClient.class); 97 | 98 | when(configServiceClient.registerClient(any(ClientRegistrationRequest.class))).thenThrow(IllegalStateException.class); 99 | 100 | try { 101 | new CommandRegisterClient("", configServiceClient, "", "").execute(); 102 | fail("Should've gotten an exception"); 103 | } catch (HystrixRuntimeException e) { 104 | Throwable cause = e.getCause(); 105 | assertTrue(cause instanceof IllegalStateException); 106 | } catch (Exception e) { 107 | fail("Should not get another exception."); 108 | } 109 | } 110 | 111 | 112 | } 113 | -------------------------------------------------------------------------------- /src/test/java/no/cantara/jau/JAUProcessTest.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import no.cantara.cs.client.ConfigurationStoreUtil; 5 | import no.cantara.cs.client.DownloadUtil; 6 | import no.cantara.cs.dto.ApplicationConfig; 7 | import no.cantara.jau.duplicatehandler.DuplicateProcessHandler; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.testng.annotations.AfterClass; 11 | import org.testng.annotations.BeforeClass; 12 | import org.testng.annotations.Test; 13 | 14 | import java.io.File; 15 | import java.util.Properties; 16 | import java.util.Scanner; 17 | import java.util.concurrent.Executors; 18 | import java.util.concurrent.ScheduledExecutorService; 19 | import java.util.concurrent.ScheduledFuture; 20 | 21 | import static java.util.concurrent.TimeUnit.MILLISECONDS; 22 | import static org.mockito.Mockito.mock; 23 | import static org.testng.Assert.assertFalse; 24 | import static org.testng.Assert.assertTrue; 25 | 26 | 27 | 28 | public class JAUProcessTest { 29 | 30 | private static final ObjectMapper mapper = new ObjectMapper(); 31 | private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); 32 | ScheduledFuture restarterHandle; 33 | private ApplicationProcess processHolder; 34 | private static final Logger log = LoggerFactory.getLogger(JAUProcessTest.class); 35 | 36 | 37 | 38 | 39 | @BeforeClass 40 | public void startServer() throws InterruptedException { 41 | DuplicateProcessHandler duplicateProcessHandler = mock(DuplicateProcessHandler.class); 42 | processHolder = new ApplicationProcess(duplicateProcessHandler); 43 | 44 | } 45 | 46 | 47 | @AfterClass 48 | public void stop() { 49 | if (processHolder != null) { 50 | processHolder.stopProcess(); 51 | } 52 | restarterHandle.cancel(true); 53 | 54 | } 55 | 56 | 57 | @Test(enabled=false) 58 | public void testProcessDownloadStartupAndRunning() throws Exception { 59 | 60 | 61 | String jsonResponse = new Scanner( new File("config1.serviceconfig") ).useDelimiter("\\A").next(); 62 | 63 | // let us type a configuration the quick way.. 64 | ApplicationConfig config = mapper.readValue(jsonResponse, ApplicationConfig.class); 65 | 66 | // Process stuff 67 | DuplicateProcessHandler duplicateProcessHandler = mock(DuplicateProcessHandler.class); 68 | ApplicationProcess processHolder= new ApplicationProcess(duplicateProcessHandler); 69 | processHolder.setWorkingDirectory(new File("./")); 70 | String workingDirectory = processHolder.getWorkingDirectory().getAbsolutePath(); 71 | 72 | 73 | // Download stuff 74 | DownloadUtil.downloadAllFiles(config.getDownloadItems(), workingDirectory); 75 | ConfigurationStoreUtil.toFiles(config.getConfigurationStores(), workingDirectory); 76 | 77 | // Lets try to start 78 | String initialCommand = config.getStartServiceScript(); 79 | int updateInterval=100; 80 | 81 | System.out.println("Initial command: "+initialCommand); 82 | processHolder.setWorkingDirectory(new File(workingDirectory)); 83 | processHolder.setCommand(initialCommand.split("\\s+")); 84 | 85 | processHolder.startProcess(); 86 | 87 | restarterHandle = scheduler.scheduleAtFixedRate( 88 | () -> { 89 | 90 | try { 91 | // Restart, whatever the reason the process is not running. 92 | if (!processHolder.processIsRunning()) { 93 | log.info("Process is not running - restarting... clientId={}, lastChanged={}, command={}", 94 | processHolder.getClientId(), processHolder.getLastChangedTimestamp(), processHolder.getCommand()); 95 | processHolder.startProcess(); 96 | } 97 | } catch (Exception e) { 98 | log.warn("Error thrown from scheduled lambda.", e); 99 | } 100 | }, 101 | 1, updateInterval, MILLISECONDS 102 | ); 103 | 104 | 105 | Thread.sleep(4000); 106 | assertTrue(processHolder.processIsRunning(), "First check"); 107 | Thread.sleep(1000); 108 | assertTrue(processHolder.processIsRunning(), "Second check"); 109 | 110 | processHolder.stopProcess(); 111 | assertFalse(processHolder.processIsRunning(), "Seventh check"); 112 | Thread.sleep(4000); 113 | assertTrue(processHolder.processIsRunning(), "Eigth check"); 114 | 115 | } 116 | 117 | private static String getStringProperty(final Properties properties, String propertyKey, String defaultValue) { 118 | String property = properties.getProperty(propertyKey, defaultValue); 119 | if (property == null) { 120 | //-Dconfigservice.url= 121 | property = System.getProperty(propertyKey); 122 | } 123 | return property; 124 | } 125 | 126 | 127 | } -------------------------------------------------------------------------------- /notinuse/MainInProcess.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau; 2 | 3 | import no.cantara.jau.serviceconfig.client.ConfigServiceClient; 4 | import no.cantara.jau.serviceconfig.client.DownloadUtil; 5 | import no.cantara.jau.serviceconfig.dto.DownloadItem; 6 | import no.cantara.jau.serviceconfig.dto.ServiceConfig; 7 | import no.cantara.jau.serviceconfig.dto.ServiceConfigSerializer; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import java.lang.reflect.Method; 12 | import java.net.URL; 13 | import java.net.URLClassLoader; 14 | import java.nio.file.Path; 15 | import java.util.concurrent.ExecutorService; 16 | import java.util.concurrent.Executors; 17 | 18 | /** 19 | * NOT WORKING! Just loose thoughts on how to run in-process. 20 | * 21 | * @author Erik Drolshammer 2015-07-13. 22 | */ 23 | public class MainInProcess { 24 | private static final Logger log = LoggerFactory.getLogger(MainInProcess.class); 25 | 26 | private final ExecutorService worker = Executors.newSingleThreadExecutor(); 27 | 28 | 29 | public static void main(String[] args) { 30 | String serviceConfigUrl = "http://localhost:7000/jau/serviceconfig/query?clientid=clientid1"; 31 | final MainInProcess main = new MainInProcess(); 32 | main.start(serviceConfigUrl); 33 | 34 | } 35 | 36 | /** 37 | * Fetch ServiceConfig 38 | * Parse ServiceConfig 39 | * Check changedTimestamp 40 | * Download 41 | * Stop existing service if running 42 | * Start new service 43 | * 44 | * @param serviceConfigUrl url to service config for this service 45 | */ 46 | public void start(String serviceConfigUrl) { 47 | String response = null; 48 | try { 49 | response = ConfigServiceClient.fetchServiceConfig(serviceConfigUrl, null, null); 50 | log.trace("fetchServiceConfig: serviceConfig={}", response); 51 | } catch (Exception e) { 52 | log.error("fetchServiceConfig failed with serviceConfigUrl={} Exiting.", serviceConfigUrl, e); 53 | System.exit(1); 54 | } 55 | 56 | //Parse 57 | ServiceConfig serviceConfig = ServiceConfigSerializer.fromJson(response); 58 | log.debug("{}", serviceConfig); 59 | 60 | 61 | //check changedTimestamp 62 | 63 | //Download 64 | Path path = null; 65 | for (DownloadItem downloadItem : serviceConfig.getDownloadItems()) { 66 | log.debug("Downloading {}", downloadItem); 67 | path = DownloadUtil.downloadFile(downloadItem, "./"); 68 | } 69 | 70 | //Stop existing service if running 71 | 72 | //Start new service 73 | 74 | //"file://./my.jar" 75 | try { 76 | URL url = path.toUri().toURL(); 77 | 78 | /* 79 | Class mainClass = getClass().getClassLoader().loadClass("net.whydah.admin.MainWithJetty"); 80 | Method method = mainClass.getDeclaredMethod("main"); 81 | Object result = method.invoke(null); 82 | */ 83 | 84 | ClassLoader loader = URLClassLoader.newInstance(new URL[]{url}, ClassLoader.getSystemClassLoader()); 85 | //possible to get Main class from manifest from jarfile? 86 | Class mainClass = Class.forName("net.whydah.admin.MainWithJetty", true, loader); 87 | 88 | 89 | // Avoid Class.newInstance, for it is evil. 90 | //Constructor constructor = clazz.getConstructor(int.class); 91 | //Object instance = constructor.newInstance(5678); 92 | Method method = mainClass.getDeclaredMethod("main", String[].class); 93 | // If the underlying method is static, then the specified obj argument is ignored. It may be null. 94 | Object result = method.invoke(null, new Object[] {new String[]{}}); 95 | 96 | 97 | 98 | /* 99 | ClassLoader loader = URLClassLoader.newInstance(new URL[]{url}, getClass().getClassLoader()); 100 | Class clazz = Class.forName("net.whydah.admin.MainWithJetty", true, loader); 101 | 102 | Class runClass = clazz.asSubclass(Runnable.class); 103 | // Avoid Class.newInstance, for it is evil. 104 | Constructor constructor = runClass.getConstructor(int.class); 105 | Runnable runnable = constructor.newInstance(5678); 106 | */ 107 | 108 | //runnable.run(); 109 | 110 | /* 111 | URLClassLoader classLoader = new URLClassLoader(new URL[]{url}, this.getClass().getClassLoader()); 112 | Class classToLoad = Class.forName("net.whydah.admin.MainWithJetty", true, classLoader); 113 | Runnable instance = (Runnable) classToLoad.newInstance(); 114 | */ 115 | //worker.submit(runnable); 116 | 117 | //Method method = classToLoad.getDeclaredMethod("myMethod"); 118 | //Object result = method.invoke(instance); 119 | } catch (Exception e) { 120 | log.error("", e); 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/no/cantara/jau/coms/CheckForUpdateHelper.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau.coms; 2 | 3 | import no.cantara.cs.client.ConfigServiceClient; 4 | import no.cantara.cs.client.EventExtractionUtil; 5 | import no.cantara.cs.client.HttpException; 6 | import no.cantara.cs.dto.CheckForUpdateRequest; 7 | import no.cantara.cs.dto.ClientConfig; 8 | import no.cantara.cs.dto.event.Event; 9 | import no.cantara.cs.dto.event.ExtractedEventsStore; 10 | import no.cantara.jau.ApplicationProcess; 11 | import no.cantara.jau.JavaAutoUpdater; 12 | import no.cantara.jau.eventextraction.EventExtractorService; 13 | import no.cantara.jau.util.ClientEnvironmentUtil; 14 | import no.cantara.jau.util.PropertiesHelper; 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | 18 | import java.net.HttpURLConnection; 19 | import java.net.SocketTimeoutException; 20 | import java.net.UnknownHostException; 21 | import java.util.List; 22 | import java.util.Properties; 23 | import java.util.SortedMap; 24 | import java.util.concurrent.ScheduledFuture; 25 | 26 | /** 27 | * Created by jorunfa on 29/10/15. 28 | */ 29 | public class CheckForUpdateHelper { 30 | 31 | private static final Logger log = LoggerFactory.getLogger(CheckForUpdateHelper.class); 32 | 33 | public static Runnable getCheckForUpdateRunnable(long interval, ConfigServiceClient configServiceClient, 34 | ApplicationProcess processHolder, 35 | ScheduledFuture processMonitorHandle, 36 | EventExtractorService extractorService, 37 | JavaAutoUpdater jau 38 | ) { 39 | return () -> { 40 | ClientConfig newClientConfig = null; 41 | try { 42 | log.debug("Checking for updates. Inside lambda."); 43 | Properties applicationState = configServiceClient.getApplicationState(); 44 | String clientId = PropertiesHelper.getStringProperty(applicationState, ConfigServiceClient.CLIENT_ID, null); 45 | String lastChanged = PropertiesHelper.getStringProperty(applicationState, ConfigServiceClient.LAST_CHANGED, null); 46 | SortedMap clientEnvironment = ClientEnvironmentUtil.getClientEnvironment(applicationState, 47 | String.valueOf(processHolder.processIsRunning())); 48 | 49 | List events = extractorService.extractEvents(); 50 | ExtractedEventsStore eventsStore = EventExtractionUtil.mapToExtractedEvents(events); 51 | 52 | CheckForUpdateRequest checkForUpdateRequest = new CheckForUpdateRequest(lastChanged, clientEnvironment, 53 | PropertiesHelper.getClientName(), eventsStore); 54 | newClientConfig = configServiceClient.checkForUpdate(clientId, checkForUpdateRequest); 55 | } catch (SocketTimeoutException | UnknownHostException e) { 56 | log.warn("checkForUpdate failed, do nothing. Fail reason {}. Retrying in {} seconds.", e.getMessage(), interval); 57 | return; 58 | } catch (HttpException e) { 59 | if (e.getStatusCode() == HttpURLConnection.HTTP_PRECON_FAILED) { 60 | log.warn("Got http {} Precondition failed, reregistering client. Response message: {}", e.getStatusCode(), e.getMessage()); 61 | configServiceClient.cleanApplicationState(); 62 | newClientConfig = jau.registerClient(); 63 | } else if (e.getStatusCode() == HttpURLConnection.HTTP_BAD_REQUEST) { 64 | log.error("Got http {} Bad request: ", e.getStatusCode(), e); 65 | return; 66 | } else if (e.getStatusCode() == HttpURLConnection.HTTP_INTERNAL_ERROR) { 67 | log.warn("Got http {} Internal error: ", e.getStatusCode(), e); 68 | return; 69 | } else { 70 | log.warn("Got http {}. checkForUpdate failed, do nothing. Retrying in {} seconds.", e.getStatusCode(), interval, e); 71 | return; 72 | } 73 | } catch (Throwable e) { 74 | log.warn("checkForUpdate failed, do nothing. Retrying in {} seconds.", interval, e); 75 | return; 76 | } 77 | 78 | // ExecutorService swallows any exceptions silently, so need to handle them explicitly. 79 | // See http://www.nurkiewicz.com/2014/11/executorservice-10-tips-and-tricks.html (point 6.). 80 | try { 81 | 82 | if (newClientConfig == null) { 83 | log.debug("No updated config."); 84 | extractorService.clearRepo(); 85 | return; 86 | } 87 | 88 | log.debug("We got changes - stopping process and downloading new files."); 89 | processHolder.stopProcess(); 90 | 91 | jau.storeClientFiles(newClientConfig); 92 | 93 | String[] command = newClientConfig.config.getStartServiceScript().split("\\s+"); 94 | processHolder.setCommand(command); 95 | processHolder.setClientId(newClientConfig.clientId); 96 | processHolder.setLastChangedTimestamp(newClientConfig.config.getLastChanged()); 97 | 98 | configServiceClient.saveApplicationState(newClientConfig); 99 | } catch (Throwable e) { 100 | log.warn("Error thrown from scheduled lambda.", e); 101 | } 102 | }; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/no/cantara/jau/util/DisableSSLVerification.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau.util; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import javax.net.ssl.HostnameVerifier; 7 | import javax.net.ssl.HttpsURLConnection; 8 | import javax.net.ssl.SSLContext; 9 | import javax.net.ssl.TrustManager; 10 | import javax.net.ssl.TrustManagerFactory; 11 | import javax.net.ssl.X509TrustManager; 12 | import java.security.KeyManagementException; 13 | import java.security.KeyStore; 14 | import java.security.KeyStoreException; 15 | import java.security.NoSuchAlgorithmException; 16 | import java.security.cert.CertificateException; 17 | import java.security.cert.X509Certificate; 18 | import java.util.List; 19 | 20 | /** 21 | * This class provides a way to disable TLS check for certain domains. Useful in the case where JAU is running inside 22 | * a network that uses a proxy etc. that intercepts TLS traffic. Inspired by https://stackoverflow.com/q/49301717 23 | */ 24 | public class DisableSSLVerification { 25 | private static final Logger log = LoggerFactory.getLogger(DisableSSLVerification.class); 26 | 27 | static class CustomTrustManager implements X509TrustManager { 28 | 29 | /* 30 | * The default X509TrustManager returned by SunX509. We'll delegate decisions to 31 | * it, and fall back to the logic in this class if the default X509TrustManager 32 | * doesn't trust it. 33 | */ 34 | private X509TrustManager sunJSSEX509TrustManager; 35 | private List domains; 36 | 37 | public CustomTrustManager(List domains) throws NoSuchAlgorithmException, KeyStoreException { 38 | TrustManagerFactory trustManagerFactory = TrustManagerFactory 39 | .getInstance(TrustManagerFactory.getDefaultAlgorithm()); 40 | 41 | trustManagerFactory.init((KeyStore) null); 42 | 43 | sunJSSEX509TrustManager = null; 44 | for (TrustManager trustManager : trustManagerFactory.getTrustManagers()) { 45 | if (trustManager instanceof X509TrustManager) { 46 | sunJSSEX509TrustManager = (X509TrustManager) trustManager; 47 | } 48 | } 49 | 50 | this.domains = domains; 51 | } 52 | 53 | public java.security.cert.X509Certificate[] getAcceptedIssuers() { 54 | return sunJSSEX509TrustManager.getAcceptedIssuers(); 55 | } 56 | 57 | @Override 58 | public void checkClientTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate, 59 | String paramString) throws CertificateException { 60 | sunJSSEX509TrustManager.checkClientTrusted(paramArrayOfX509Certificate, paramString); 61 | } 62 | 63 | @Override 64 | public void checkServerTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate, 65 | String paramString) throws CertificateException { 66 | try { 67 | sunJSSEX509TrustManager.checkServerTrusted(paramArrayOfX509Certificate, paramString); 68 | } catch (CertificateException e) { 69 | for (X509Certificate a : paramArrayOfX509Certificate) { 70 | for (String domain : domains) { 71 | if (("CN=" + domain).equals(a.getSubjectDN().getName())) { 72 | log.trace("Certificate for " + a.getSubjectDN().getName() 73 | + " accepted, although Java TrustManager did not accept it"); 74 | return; 75 | } 76 | } 77 | } 78 | throw new CertificateException("Certificate verification finally failed", e); 79 | } 80 | } 81 | } 82 | 83 | /** 84 | * Disables certificate validation for any HTTPS request by this application to given domains 85 | * 86 | * @param domains list of domains to ignore TLS certificate validation of. Note that each domain given is checked 87 | * against subject DN of certificate, and as such if a wildcard certificate is expected at the domain 88 | * to ignore, it must be specified as e.g. "*.somedomain.com" 89 | */ 90 | public static void disableForDomains(List domains) { 91 | log.warn("Overriding TLS certificate verification. Note that this means any certificate for the given " + 92 | "domains are accepted even if a certificate is not trusted by the JVM! Be careful and only use if " + 93 | "absolutely necessary! domains: {}", domains); 94 | TrustManager[] trustAllCerts; 95 | try { 96 | trustAllCerts = new TrustManager[]{new CustomTrustManager(domains)}; 97 | } catch (NoSuchAlgorithmException | KeyStoreException e) { 98 | throw new RuntimeException("Failed to create a trustmanager", e); 99 | } 100 | 101 | // Install the all-trusting trust manager 102 | SSLContext sc = null; 103 | try { 104 | sc = SSLContext.getInstance("SSL"); 105 | sc.init(null, trustAllCerts, new java.security.SecureRandom()); 106 | } catch (NoSuchAlgorithmException | KeyManagementException e) { 107 | throw new RuntimeException("Failed to install new trust store", e); 108 | } 109 | HostnameVerifier allHostsValid = (hostname, session) -> { 110 | for (String domain : domains) { 111 | if (hostname.equals(domain)) { 112 | log.info("Override hostname verification for: " + hostname); 113 | return true; 114 | } 115 | } 116 | return false; 117 | }; 118 | HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid); 119 | HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/no/cantara/jau/duplicatehandler/DuplicateProcessHandler.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau.duplicatehandler; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.io.*; 7 | import java.util.regex.Matcher; 8 | import java.util.regex.Pattern; 9 | 10 | /** 11 | * Makes sure any running managed service is killed if JAU should restart 12 | */ 13 | public class DuplicateProcessHandler { 14 | private static final Logger log = LoggerFactory.getLogger(DuplicateProcessHandler.class); 15 | public static final String RUNNING_PROCESS_FILENAME = "last-running-process.txt"; 16 | private LastRunningProcessFileUtil fileUtil; 17 | private ProcessExecutor processExecutor; 18 | 19 | public DuplicateProcessHandler(ProcessExecutorFetcher processExecutorFetcher, LastRunningProcessFileUtil fileUtil) { 20 | this.fileUtil = fileUtil; 21 | processExecutor = processExecutorFetcher.getProcessExecutorBasedOnOs(); 22 | } 23 | 24 | /** 25 | * Returns true if process is killed or process is not running. Returns false if any error 26 | * @param processCommand The command used to launch the application, e.g. "java -jar example-application.jar" 27 | */ 28 | public boolean killExistingProcessIfRunning(String processCommand) { 29 | String pid; 30 | try { 31 | pid = fileUtil.getRunningProcessPidFromFile(); 32 | } catch (IOException e) { 33 | log.warn("Could not read file={}. Attempting to kill process by searching for processes with " + 34 | "processCommand={}", fileUtil.getFileName(), processCommand); 35 | return findRunningProcessByProcessNameAndKill(processCommand); 36 | } 37 | if (pid != null) { 38 | if (isValidPid(pid) && findRunningProcessByPIDAndKill(pid)) { 39 | return true; 40 | } else { 41 | return findRunningProcessByProcessNameAndKill(processCommand); 42 | } 43 | } else { 44 | log.info("{} not found. Assuming no existing managed process is running.", fileUtil.getFileName()); 45 | return true; 46 | } 47 | } 48 | 49 | public void findRunningManagedProcessPidAndWriteToFile(Process managedProcess) { 50 | String pid = findProcessId(managedProcess); 51 | if (pid != null) { 52 | fileUtil.writePidToFile(pid); 53 | } else { 54 | log.error("Did not find process id of running managed process!"); 55 | } 56 | } 57 | 58 | private boolean findRunningProcessByPIDAndKill(String pid) { 59 | try { 60 | if (processIsRunning(pid)) { 61 | log.info("Last recorded managed process pid={} is running", pid); 62 | return killRunningProcessByPID(pid); 63 | } else { 64 | log.info("Last recorded managed process pid={} is not running.", pid); 65 | return true; 66 | } 67 | } catch (IOException e) { 68 | log.error("Exception executing process. Could not check if process with pid={} is running", pid, e); 69 | } catch (InterruptedException e) { 70 | log.error("Process interrupted. Could not check if process with pid={} is running", pid, e); 71 | } 72 | return false; 73 | } 74 | 75 | private boolean findRunningProcessByProcessNameAndKill(String processCommand) { 76 | String processName = extractProcessNameFromCommand(processCommand); 77 | 78 | if (processName != null) { 79 | return killRunningProcessByProcessName(processName); 80 | } 81 | 82 | return false; 83 | } 84 | 85 | private boolean killRunningProcessByProcessName(String processName) { 86 | try { 87 | return processExecutor.killProcessByProcessName(processName); 88 | } catch (IOException | InterruptedException e) { 89 | log.error("Could not kill process by process name", e); 90 | } 91 | return false; 92 | } 93 | 94 | public String extractProcessNameFromCommand(String processCommand) { 95 | String regex = "^\\S+\\.[A-Za-z]{3}$"; 96 | String[] wordsInProcessCommand = processCommand.split(" "); 97 | Pattern pattern = Pattern.compile(regex); 98 | for (String word : wordsInProcessCommand) { 99 | Matcher matcher = pattern.matcher(word); 100 | if (matcher.find()) 101 | { 102 | String fileName = matcher.group(0); 103 | log.debug("Extracted name={} of old running process from command={}", fileName, processCommand); 104 | return fileName; 105 | } 106 | } 107 | log.error("No matching name of old running process found in command={} using regex={}", processCommand, regex); 108 | return null; 109 | } 110 | 111 | private boolean processIsRunning(String pid) throws IOException, InterruptedException { 112 | return processExecutor.isProcessRunning(pid); 113 | } 114 | 115 | private boolean killRunningProcessByPID(String pid) { 116 | try { 117 | boolean processWasKilled = processExecutor.killProcessByPID(pid); 118 | if (processWasKilled) { 119 | log.info("Successfully killed existing running managed process pid={}", pid); 120 | return true; 121 | } 122 | } catch (IOException e) { 123 | log.error("Exception executing kill process. Could not kill running managed process pid={}", pid, e); 124 | 125 | } catch (InterruptedException e) { 126 | log.error("Kill process was interrupted. Could not kill running managed process pid={}", pid, e); 127 | } 128 | return false; 129 | } 130 | 131 | private String findProcessId(Process managedProcess) { 132 | try { 133 | return processExecutor.findProcessId(managedProcess); 134 | } catch (ReflectiveOperationException e) { 135 | log.error("Could not find pid of managed process", e); 136 | } 137 | return null; 138 | } 139 | 140 | private static boolean isValidPid(String pid) { 141 | // TODO: Is this check of valid PID too naive? 142 | try { 143 | Long.parseLong(pid); 144 | return true; 145 | } catch (NumberFormatException e) { 146 | log.warn("PID is not valid number. Got: '{}' {}", pid, e); 147 | return false; 148 | } 149 | } 150 | 151 | 152 | } -------------------------------------------------------------------------------- /src/test/java/no/cantara/jau/coms/CheckForUpdateHelperTest.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau.coms; 2 | 3 | import no.cantara.cs.client.ConfigServiceClient; 4 | import no.cantara.cs.client.HttpException; 5 | import no.cantara.cs.dto.ApplicationConfig; 6 | import no.cantara.cs.dto.CheckForUpdateRequest; 7 | import no.cantara.cs.dto.ClientConfig; 8 | import no.cantara.jau.ApplicationProcess; 9 | import no.cantara.jau.JavaAutoUpdater; 10 | import no.cantara.jau.eventextraction.EventExtractorService; 11 | import org.testng.annotations.Test; 12 | 13 | import java.io.IOException; 14 | import java.net.HttpURLConnection; 15 | import java.util.Properties; 16 | import java.util.concurrent.ScheduledFuture; 17 | 18 | import static org.mockito.Mockito.*; 19 | 20 | /** 21 | * Created by jorunfa on 29/10/15. 22 | */ 23 | public class CheckForUpdateHelperTest { 24 | 25 | @Test 26 | public void testPreconditionFailed() throws IOException { 27 | ConfigServiceClient configServiceClient = mock(ConfigServiceClient.class); 28 | when(configServiceClient.getApplicationState()).thenReturn(new Properties()); 29 | when(configServiceClient.checkForUpdate(any(), any())).thenThrow(new HttpException(HttpURLConnection.HTTP_PRECON_FAILED, "precondition failed")); 30 | 31 | ApplicationProcess processHolder = mock(ApplicationProcess.class); 32 | ScheduledFuture processMonitorHandle = mock(ScheduledFuture.class); 33 | JavaAutoUpdater jau = mock(JavaAutoUpdater.class); 34 | 35 | ApplicationConfig newConfig = new ApplicationConfig("name"); 36 | newConfig.setStartServiceScript("start script"); 37 | ClientConfig newClientConfig = new ClientConfig("clientId", newConfig); 38 | when(jau.registerClient()).thenReturn(newClientConfig); 39 | 40 | EventExtractorService extractorService = mock(EventExtractorService.class); 41 | 42 | Runnable checkForUpdateRunnable = CheckForUpdateHelper.getCheckForUpdateRunnable(1, configServiceClient, 43 | processHolder, processMonitorHandle, extractorService, jau 44 | ); 45 | checkForUpdateRunnable.run(); 46 | 47 | verify(jau).registerClient(); 48 | verify(configServiceClient).cleanApplicationState(); 49 | verify(jau).storeClientFiles(newClientConfig); 50 | verify(configServiceClient).saveApplicationState(newClientConfig); 51 | } 52 | 53 | @Test 54 | public void testNotChanged() throws IOException { 55 | ConfigServiceClient configServiceClient = mock(ConfigServiceClient.class); 56 | when(configServiceClient.getApplicationState()).thenReturn(new Properties()); 57 | when(configServiceClient.checkForUpdate(anyString(), any(CheckForUpdateRequest.class))).thenReturn(null); 58 | 59 | ApplicationProcess processHolder = mock(ApplicationProcess.class); 60 | ScheduledFuture processMonitorHandle = mock(ScheduledFuture.class); 61 | JavaAutoUpdater jau = mock(JavaAutoUpdater.class); 62 | EventExtractorService extractorService = mock(EventExtractorService.class); 63 | 64 | Runnable checkForUpdateRunnable = CheckForUpdateHelper.getCheckForUpdateRunnable(1, configServiceClient, 65 | processHolder, processMonitorHandle, extractorService, jau); 66 | checkForUpdateRunnable.run(); 67 | 68 | verify(processHolder, never()).stopProcess(); 69 | } 70 | 71 | @Test 72 | public void testServerProblems() throws IOException { 73 | ConfigServiceClient configServiceClient = mock(ConfigServiceClient.class); 74 | when(configServiceClient.getApplicationState()).thenReturn(new Properties()); 75 | when(configServiceClient.checkForUpdate(anyString(), any(CheckForUpdateRequest.class))).thenThrow(new HttpException(HttpURLConnection.HTTP_INTERNAL_ERROR, "internal error")); 76 | 77 | ApplicationProcess processHolder = mock(ApplicationProcess.class); 78 | ScheduledFuture processMonitorHandle = mock(ScheduledFuture.class); 79 | JavaAutoUpdater jau = mock(JavaAutoUpdater.class); 80 | EventExtractorService extractorService = mock(EventExtractorService.class); 81 | 82 | Runnable checkForUpdateRunnable = CheckForUpdateHelper.getCheckForUpdateRunnable(1, configServiceClient, 83 | processHolder, processMonitorHandle, extractorService, jau); 84 | checkForUpdateRunnable.run(); 85 | 86 | verify(processHolder, never()).stopProcess(); 87 | } 88 | 89 | @Test 90 | public void testUnknownHttpError() throws IOException { 91 | ConfigServiceClient configServiceClient = mock(ConfigServiceClient.class); 92 | when(configServiceClient.getApplicationState()).thenReturn(new Properties()); 93 | when(configServiceClient.checkForUpdate(anyString(), any(CheckForUpdateRequest.class))).thenThrow(new HttpException(HttpURLConnection.HTTP_BAD_GATEWAY, "bad gateway")); 94 | 95 | ApplicationProcess processHolder = mock(ApplicationProcess.class); 96 | ScheduledFuture processMonitorHandle = mock(ScheduledFuture.class); 97 | JavaAutoUpdater jau = mock(JavaAutoUpdater.class); 98 | EventExtractorService extractorService = mock(EventExtractorService.class); 99 | 100 | Runnable checkForUpdateRunnable = CheckForUpdateHelper.getCheckForUpdateRunnable(1, configServiceClient, 101 | processHolder, processMonitorHandle, extractorService, jau); 102 | checkForUpdateRunnable.run(); 103 | 104 | verify(processHolder, never()).stopProcess(); 105 | } 106 | 107 | @Test 108 | public void testIOConnection() throws IOException { 109 | ConfigServiceClient configServiceClient = mock(ConfigServiceClient.class); 110 | when(configServiceClient.getApplicationState()).thenReturn(new Properties()); 111 | when(configServiceClient.checkForUpdate(anyString(), any(CheckForUpdateRequest.class))).thenThrow(new IOException()); 112 | 113 | ApplicationProcess processHolder = mock(ApplicationProcess.class); 114 | ScheduledFuture processMonitorHandle = mock(ScheduledFuture.class); 115 | JavaAutoUpdater jau = mock(JavaAutoUpdater.class); 116 | EventExtractorService extractorService = mock(EventExtractorService.class); 117 | 118 | Runnable checkForUpdateRunnable = CheckForUpdateHelper.getCheckForUpdateRunnable(1, configServiceClient, 119 | processHolder, processMonitorHandle, extractorService, jau); 120 | checkForUpdateRunnable.run(); 121 | 122 | verify(processHolder, never()).stopProcess(); 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /src/test/java/no/cantara/jau/eventextraction/EventExtractorTest.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau.eventextraction; 2 | 3 | import java.io.BufferedWriter; 4 | import java.io.FileOutputStream; 5 | import java.io.IOException; 6 | import java.io.OutputStreamWriter; 7 | import java.nio.file.Files; 8 | import java.nio.file.Paths; 9 | import java.util.ArrayList; 10 | import java.util.Collections; 11 | import java.util.List; 12 | 13 | import org.testng.annotations.AfterMethod; 14 | import org.testng.annotations.BeforeMethod; 15 | import org.testng.annotations.Test; 16 | 17 | import no.cantara.cs.dto.event.Event; 18 | import no.cantara.cs.dto.event.EventExtractionTag; 19 | 20 | import static org.testng.Assert.assertEquals; 21 | import static org.testng.Assert.assertNotEquals; 22 | 23 | public class EventExtractorTest { 24 | 25 | private final static String LOG_FILE_PATH = "event-extractor-test-log-file.log"; 26 | private final static String LOG_FILE_PROPERTIES_PATH = "event-extractor-test-log-file.log.properties"; 27 | 28 | private String regularLogLine; 29 | private String errorLogLine; 30 | private String mdcLogLine; 31 | private String mdcLogLineNotOwnWord; 32 | private String exceptionLogLine; 33 | private String continuedLogLine; 34 | private EventRepo repo; 35 | private List tags; 36 | 37 | @BeforeMethod 38 | public void createFileWithEvents() throws Exception { 39 | Files.deleteIfExists(Paths.get(LOG_FILE_PROPERTIES_PATH)); 40 | 41 | regularLogLine = "17:16:26.828 [main] TRACE n.n.pharmacy.config.ApplicationMode - isDemoMode: false"; 42 | errorLogLine = "12:34:45.035 [pool-1-thread-1] ERROR n.c.jau.coms.CheckForUpdateHelper - checkForUpdate failed, do nothing. Retrying in 60 seconds."; 43 | mdcLogLine = "13:28:13.343 this-is-an-mdc-tag [pool-1-thread-1] DEBUG no.cantara.jau.JavaAutoUpdater - Checking if process is running..."; 44 | mdcLogLineNotOwnWord = "13:28:11.801 [main] DEBUG no.cantara.jau.Main - Resolved clientName=local-jau and hello_tags"; 45 | exceptionLogLine = "17:18:11.666 [main] DEBUG n.n.p.config.PharmacyIdService - Exception cause getMessage=Could not get JDBC Connection; nested exception is java.sql.SQLException: Network error IOException: Connection refused"; 46 | continuedLogLine = "Caused by NullPointerException"; 47 | 48 | addLine(regularLogLine); 49 | addLine(errorLogLine); 50 | addLine(mdcLogLine); 51 | addLine(mdcLogLineNotOwnWord); 52 | addLine(exceptionLogLine); 53 | addLine(continuedLogLine); 54 | 55 | repo = new EventRepo(); 56 | tags = Collections.singletonList(new EventExtractionTag("this-is-an-mdc-tag", "\\bthis-is-an-mdc-tag\\b", LOG_FILE_PATH)); 57 | } 58 | 59 | private void addLine(String line) throws Exception { 60 | try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter( 61 | new FileOutputStream(LOG_FILE_PATH, true), "utf-8"))) { 62 | writer.write(line); 63 | writer.newLine(); 64 | } 65 | } 66 | 67 | @AfterMethod 68 | public void deleteTestFile() throws IOException { 69 | Files.deleteIfExists(Paths.get(LOG_FILE_PATH)); 70 | Files.deleteIfExists(Paths.get(LOG_FILE_PROPERTIES_PATH)); 71 | } 72 | 73 | @Test 74 | public void shouldExtractEventsBasedOnTags() throws Exception { 75 | EventExtractor extractor = new EventExtractor(repo, tags, LOG_FILE_PATH, "jau", null); 76 | extractor.call(); 77 | 78 | List eventsExtracted = repo.getEvents(); 79 | 80 | assertEquals(eventsExtracted.size(), 4); 81 | 82 | assertEquals(eventsExtracted.get(0).getLine(), errorLogLine); 83 | assertEquals(eventsExtracted.get(0).getTag(), "ERROR"); 84 | 85 | assertEquals(eventsExtracted.get(1).getLine(), mdcLogLine); 86 | assertEquals(eventsExtracted.get(1).getTag(), "this-is-an-mdc-tag"); 87 | 88 | assertEquals(eventsExtracted.get(2).getLine(), exceptionLogLine); 89 | assertEquals(eventsExtracted.get(2).getTag(), "Exception"); 90 | 91 | assertEquals(eventsExtracted.get(3).getLine(), continuedLogLine); 92 | assertEquals(eventsExtracted.get(3).getTag(), "Exception"); 93 | } 94 | 95 | @Test 96 | public void shouldCollateLines() throws Exception { 97 | EventExtractor extractor = new EventExtractor(repo, tags, LOG_FILE_PATH, "jau", "\\d{2}:\\d{2}:\\d{2}\\.\\d{3}"); 98 | extractor.call(); 99 | 100 | List eventsExtracted = repo.getEvents(); 101 | 102 | assertEquals(eventsExtracted.size(), 3); 103 | 104 | assertEquals(eventsExtracted.get(0).getLine(), errorLogLine); 105 | assertEquals(eventsExtracted.get(0).getTag(), "ERROR"); 106 | 107 | assertEquals(eventsExtracted.get(1).getLine(), mdcLogLine); 108 | assertEquals(eventsExtracted.get(1).getTag(), "this-is-an-mdc-tag"); 109 | 110 | assertEquals(eventsExtracted.get(2).getLine(), exceptionLogLine + "\n" + continuedLogLine); 111 | assertEquals(eventsExtracted.get(2).getTag(), "Exception"); 112 | } 113 | 114 | @Test 115 | public void shouldNotExtractEventsWhenFileIsUnmodified() throws Exception { 116 | EventRepo repo = new EventRepo(); 117 | List tags = new ArrayList<>(); 118 | tags.add(new EventExtractionTag("this-is-an-mdc-tag", "\\bthis-is-an-mdc-tag\\b", LOG_FILE_PATH)); 119 | EventExtractor extractor = new EventExtractor(repo, tags, LOG_FILE_PATH, "jau", null); 120 | 121 | extractor.call(); 122 | 123 | List eventsExtracted = repo.getEvents(); 124 | assertEquals(eventsExtracted.size(), 4); 125 | 126 | extractor.call(); 127 | 128 | eventsExtracted = repo.getEvents(); 129 | assertEquals(eventsExtracted.size(), 4); 130 | } 131 | 132 | @Test 133 | public void shouldRememberState() throws Exception { 134 | EventExtractor extractor = new EventExtractor(repo, tags, LOG_FILE_PATH, "jau", null); 135 | extractor.call(); 136 | 137 | List eventsExtracted = repo.getEvents(); 138 | assertEquals(eventsExtracted.size(), 4); 139 | 140 | // Add a line, then create new extractor. 141 | Thread.sleep(1000); // To make sure last-modified date of file changes. 142 | repo.clearEvents(); 143 | addLine(errorLogLine); 144 | extractor = new EventExtractor(repo, tags, LOG_FILE_PATH, "jau", null); 145 | extractor.call(); 146 | 147 | eventsExtracted = repo.getEvents(); 148 | assertEquals(eventsExtracted.size(), 1); 149 | assertEquals(eventsExtracted.get(0).getLine(), errorLogLine); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/test/java/no/cantara/jau/duplicatehandler/DuplicateProcessHandlerIntTest.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau.duplicatehandler; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.testng.Assert; 6 | import org.testng.annotations.BeforeMethod; 7 | import org.testng.annotations.Test; 8 | 9 | import java.io.*; 10 | import java.lang.reflect.Field; 11 | import java.nio.file.Files; 12 | import java.nio.file.Paths; 13 | 14 | public class DuplicateProcessHandlerIntTest { 15 | private static final Logger log = LoggerFactory.getLogger(DuplicateProcessHandlerIntTest.class); 16 | private static final String PROCESS_COMMAND = "java -jar pharmacy-agent-0.8-SNAPSHOT.jar";; 17 | private static final String TEST_PID_FILE = "target/test-pid.txt"; 18 | 19 | @BeforeMethod 20 | public void cleanup() throws IOException { 21 | deleteTestRunningProcessFile(); 22 | } 23 | 24 | @Test 25 | public void shouldKillExistingProcessWhenExistingProcessIsRunning() throws IOException, 26 | NoSuchFieldException, IllegalAccessException, InterruptedException { 27 | LastRunningProcessFileUtil fileUtil = new LastRunningProcessFileUtil(TEST_PID_FILE); 28 | DuplicateProcessHandler duplicateProcessHandler = new DuplicateProcessHandler( 29 | new ProcessExecutorFetcher(), fileUtil); 30 | Process p = createDummyProcess(); 31 | int PID = getPIDFromProcess(p); 32 | createFileAndWriteLine(PID + ""); 33 | 34 | boolean processWasKilled = duplicateProcessHandler.killExistingProcessIfRunning(PROCESS_COMMAND); 35 | boolean processIsRunning = checkIfProcessIsRunning(PID); 36 | 37 | Assert.assertEquals(processWasKilled, !processIsRunning); 38 | } 39 | 40 | //Tries to kill a process with given PID, so disabled for CI tool. 41 | //Cannot be certain that process with PID does not exists 42 | @Test(enabled = false) 43 | public void shouldReturnTrueWhenTryingToKillNonRunningProcess() throws IOException, InterruptedException { 44 | LastRunningProcessFileUtil fileUtil = new LastRunningProcessFileUtil(TEST_PID_FILE); 45 | DuplicateProcessHandler duplicateProcessHandler = new DuplicateProcessHandler( 46 | new ProcessExecutorFetcher(), fileUtil); 47 | int PID = 987654; 48 | 49 | boolean processWasKilled = duplicateProcessHandler.killExistingProcessIfRunning(PROCESS_COMMAND); 50 | boolean processIsRunning = checkIfProcessIsRunning(PID); 51 | 52 | Assert.assertTrue(processWasKilled); 53 | Assert.assertFalse(processIsRunning); 54 | } 55 | 56 | @Test 57 | public void shouldNotKillProcessWhenPidIsNotValid() throws IOException, InterruptedException { 58 | LastRunningProcessFileUtil fileUtil = new LastRunningProcessFileUtil(TEST_PID_FILE); 59 | DuplicateProcessHandler duplicateProcessHandler = new DuplicateProcessHandler( 60 | new ProcessExecutorFetcher(), fileUtil); 61 | createFileAndWriteLine("notvalidpid"); 62 | 63 | boolean processWasKilled = duplicateProcessHandler.killExistingProcessIfRunning(PROCESS_COMMAND); 64 | 65 | Assert.assertFalse(processWasKilled); 66 | } 67 | 68 | @Test 69 | public void shouldWritePIDToFile() throws IOException, NoSuchFieldException, IllegalAccessException { 70 | LastRunningProcessFileUtil fileUtil = new LastRunningProcessFileUtil(TEST_PID_FILE); 71 | DuplicateProcessHandler duplicateProcessHandler = new DuplicateProcessHandler( 72 | new ProcessExecutorFetcher(), fileUtil); 73 | Process currentProcess = createDummyProcess(); 74 | long PID = getPIDFromProcess(currentProcess); 75 | duplicateProcessHandler.findRunningManagedProcessPidAndWriteToFile(currentProcess); 76 | 77 | String pid = new String(Files.readAllBytes(Paths.get(TEST_PID_FILE))); 78 | 79 | Assert.assertEquals(pid, Long.toString(PID)); 80 | } 81 | 82 | @Test 83 | public void shouldReturnTrueIfRunningProcessFileDoesNotExist() throws IOException { 84 | LastRunningProcessFileUtil fileUtil = new LastRunningProcessFileUtil(TEST_PID_FILE); 85 | DuplicateProcessHandler duplicateProcessHandler = new DuplicateProcessHandler( 86 | new ProcessExecutorFetcher(), fileUtil); 87 | 88 | boolean processWasKilled = duplicateProcessHandler.killExistingProcessIfRunning(PROCESS_COMMAND); 89 | 90 | Assert.assertTrue(processWasKilled); 91 | } 92 | 93 | @Test 94 | public void shouldOverwriteFileWhenWritingPIDToFile() throws IOException, NoSuchFieldException, IllegalAccessException { 95 | LastRunningProcessFileUtil fileUtil = new LastRunningProcessFileUtil(TEST_PID_FILE); 96 | DuplicateProcessHandler duplicateProcessHandler = new DuplicateProcessHandler( 97 | new ProcessExecutorFetcher(), fileUtil); 98 | Process currentProcess = createDummyProcess(); 99 | long firstPid = getPIDFromProcess(currentProcess); 100 | currentProcess = createDummyProcess(); 101 | long secondPid = getPIDFromProcess(currentProcess); 102 | duplicateProcessHandler.findRunningManagedProcessPidAndWriteToFile(currentProcess); 103 | 104 | String pid = new String(Files.readAllBytes(Paths.get(TEST_PID_FILE))); 105 | 106 | Assert.assertEquals(pid, Long.toString(secondPid)); 107 | } 108 | 109 | private static boolean checkIfProcessIsRunning(long PID) throws IOException, InterruptedException { 110 | ProcessBuilder processBuilder; 111 | if (System.getProperty("os.name").toLowerCase().contains("windows")) { 112 | processBuilder = new ProcessBuilder("taskkill", Long.toString(PID)); 113 | } else { //unix 114 | processBuilder = new ProcessBuilder("kill", "-9", Long.toString(PID)); 115 | } 116 | 117 | Process p = processBuilder.start(); 118 | p.waitFor(); 119 | if (p.exitValue() == 0) { 120 | return true; 121 | } 122 | return false; 123 | } 124 | 125 | private static Process createDummyProcess() throws IOException, NoSuchFieldException, IllegalAccessException { 126 | return Runtime.getRuntime().exec("sleep 4"); 127 | } 128 | 129 | private static int getPIDFromProcess(Process process) throws IllegalAccessException, NoSuchFieldException { 130 | Field f = process.getClass().getDeclaredField("pid"); 131 | f.setAccessible(true); 132 | int PID = f.getInt(process); 133 | return PID; 134 | } 135 | 136 | private static void createFileAndWriteLine(String lineToWrite) throws IOException { 137 | Files.createFile(Paths.get(TEST_PID_FILE)); 138 | 139 | try (Writer writer = new BufferedWriter(new OutputStreamWriter( 140 | new FileOutputStream(TEST_PID_FILE), "utf-8"))) { 141 | writer.write(lineToWrite); 142 | } 143 | } 144 | 145 | 146 | private static void deleteTestRunningProcessFile() throws IOException { 147 | Files.deleteIfExists(Paths.get(TEST_PID_FILE)); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/util/backoff/ExponentialBackOff.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002-2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.util.backoff; 18 | 19 | /** 20 | * Implementation of {@link BackOff} that increases the back off period for each 21 | * retry attempt. When the interval has reached the {@link #setMaxInterval(long) 22 | * max interval}, it is no longer increased. Stops retrying once the 23 | * {@link #setMaxElapsedTime(long) max elapsed time} has been reached. 24 | * 25 | *

Example: The default interval is {@value #DEFAULT_INITIAL_INTERVAL} ms, 26 | * the default multiplier is {@value #DEFAULT_MULTIPLIER}, and the default max 27 | * interval is {@value #DEFAULT_MAX_INTERVAL}. For 10 attempts the sequence will be 28 | * as follows: 29 | * 30 | *

 31 |  * request#     back off
 32 |  *
 33 |  *  1              2000
 34 |  *  2              3000
 35 |  *  3              4500
 36 |  *  4              6750
 37 |  *  5             10125
 38 |  *  6             15187
 39 |  *  7             22780
 40 |  *  8             30000
 41 |  *  9             30000
 42 |  * 10             30000
 43 |  * 
44 | * 45 | *

Note that the default max elapsed time is {@link Long#MAX_VALUE}. Use 46 | * {@link #setMaxElapsedTime(long)} to limit the maximum length of time 47 | * that an instance should accumulate before returning 48 | * {@link BackOffExecution#STOP}. 49 | * 50 | * @author Stephane Nicoll 51 | * @since 4.1 52 | */ 53 | public class ExponentialBackOff implements BackOff { 54 | 55 | /** 56 | * The default initial interval. 57 | */ 58 | public static final long DEFAULT_INITIAL_INTERVAL = 2000L; 59 | 60 | /** 61 | * The default multiplier (increases the interval by 50%). 62 | */ 63 | public static final double DEFAULT_MULTIPLIER = 1.5; 64 | 65 | /** 66 | * The default maximum back off time. 67 | */ 68 | public static final long DEFAULT_MAX_INTERVAL = 30000L; 69 | 70 | /** 71 | * The default maximum elapsed time. 72 | */ 73 | public static final long DEFAULT_MAX_ELAPSED_TIME = Long.MAX_VALUE; 74 | 75 | 76 | private long initialInterval = DEFAULT_INITIAL_INTERVAL; 77 | 78 | private double multiplier = DEFAULT_MULTIPLIER; 79 | 80 | private long maxInterval = DEFAULT_MAX_INTERVAL; 81 | 82 | private long maxElapsedTime = DEFAULT_MAX_ELAPSED_TIME; 83 | 84 | 85 | /** 86 | * Create an instance with the default settings. 87 | * @see #DEFAULT_INITIAL_INTERVAL 88 | * @see #DEFAULT_MULTIPLIER 89 | * @see #DEFAULT_MAX_INTERVAL 90 | * @see #DEFAULT_MAX_ELAPSED_TIME 91 | */ 92 | public ExponentialBackOff() { 93 | } 94 | 95 | /** 96 | * Create an instance with the supplied settings. 97 | * @param initialInterval the initial interval in milliseconds 98 | * @param multiplier the multiplier (should be greater than or equal to 1) 99 | */ 100 | public ExponentialBackOff(long initialInterval, double multiplier) { 101 | checkMultiplier(multiplier); 102 | this.initialInterval = initialInterval; 103 | this.multiplier = multiplier; 104 | } 105 | 106 | /** 107 | * The initial interval in milliseconds. 108 | */ 109 | public void setInitialInterval(long initialInterval) { 110 | this.initialInterval = initialInterval; 111 | } 112 | 113 | /** 114 | * Return the initial interval in milliseconds. 115 | */ 116 | public long getInitialInterval() { 117 | return initialInterval; 118 | } 119 | 120 | /** 121 | * The value to multiply the current interval by for each retry attempt. 122 | */ 123 | public void setMultiplier(double multiplier) { 124 | checkMultiplier(multiplier); 125 | this.multiplier = multiplier; 126 | } 127 | 128 | /** 129 | * Return the value to multiply the current interval by for each retry attempt. 130 | */ 131 | public double getMultiplier() { 132 | return multiplier; 133 | } 134 | 135 | /** 136 | * The maximum back off time. 137 | */ 138 | public void setMaxInterval(long maxInterval) { 139 | this.maxInterval = maxInterval; 140 | } 141 | 142 | /** 143 | * Return the maximum back off time. 144 | */ 145 | public long getMaxInterval() { 146 | return maxInterval; 147 | } 148 | 149 | /** 150 | * The maximum elapsed time in milliseconds after which a call to 151 | * {@link BackOffExecution#nextBackOff()} returns {@link BackOffExecution#STOP}. 152 | */ 153 | public void setMaxElapsedTime(long maxElapsedTime) { 154 | this.maxElapsedTime = maxElapsedTime; 155 | } 156 | 157 | /** 158 | * Return the maximum elapsed time in milliseconds after which a call to 159 | * {@link BackOffExecution#nextBackOff()} returns {@link BackOffExecution#STOP}. 160 | */ 161 | public long getMaxElapsedTime() { 162 | return maxElapsedTime; 163 | } 164 | 165 | @Override 166 | public BackOffExecution start() { 167 | return new ExponentialBackOffExecution(); 168 | } 169 | 170 | private void checkMultiplier(double multiplier) { 171 | if (multiplier < 1) { 172 | throw new IllegalArgumentException("Invalid multiplier '" + multiplier + "'. Should be equal" + 173 | "or higher than 1. A multiplier of 1 is equivalent to a fixed interval"); 174 | } 175 | } 176 | 177 | 178 | private class ExponentialBackOffExecution implements BackOffExecution { 179 | 180 | private long currentInterval = -1; 181 | 182 | private long currentElapsedTime = 0; 183 | 184 | @Override 185 | public long nextBackOff() { 186 | if (currentElapsedTime >= maxElapsedTime) { 187 | return STOP; 188 | } 189 | 190 | long nextInterval = computeNextInterval(); 191 | currentElapsedTime += nextInterval; 192 | return nextInterval; 193 | } 194 | 195 | private long computeNextInterval() { 196 | long maxInterval = getMaxInterval(); 197 | if (this.currentInterval >= maxInterval) { 198 | return maxInterval; 199 | } 200 | else if (this.currentInterval < 0) { 201 | long initialInterval = getInitialInterval(); 202 | this.currentInterval = (initialInterval < maxInterval 203 | ? initialInterval : maxInterval); 204 | } 205 | else { 206 | this.currentInterval = multiplyInterval(maxInterval); 207 | } 208 | return currentInterval; 209 | } 210 | 211 | private long multiplyInterval(long maxInterval) { 212 | long i = this.currentInterval; 213 | i *= getMultiplier(); 214 | return (i > maxInterval ? maxInterval : i); 215 | } 216 | 217 | 218 | @Override 219 | public String toString() { 220 | String i = (this.currentInterval < 0 ? "n/a" : this.currentInterval + "ms"); 221 | final StringBuilder sb = new StringBuilder("ExponentialBackOff{"); 222 | sb.append("currentInterval=").append(i); 223 | sb.append(", multiplier=").append(getMultiplier()); 224 | sb.append('}'); 225 | return sb.toString(); 226 | } 227 | } 228 | 229 | } 230 | -------------------------------------------------------------------------------- /src/main/java/no/cantara/jau/eventextraction/CommandExtractEventsFromFile.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau.eventextraction; 2 | 3 | import com.netflix.hystrix.HystrixCommand; 4 | import com.netflix.hystrix.HystrixCommandGroupKey; 5 | import com.netflix.hystrix.HystrixCommandProperties; 6 | import no.cantara.cs.dto.event.Event; 7 | import no.cantara.cs.dto.event.EventExtractionTag; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import java.io.*; 12 | import java.nio.charset.StandardCharsets; 13 | import java.nio.file.Files; 14 | import java.nio.file.Paths; 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | import java.util.regex.Matcher; 18 | import java.util.regex.Pattern; 19 | 20 | public class CommandExtractEventsFromFile extends HystrixCommand { 21 | private static final Logger log = LoggerFactory.getLogger(CommandExtractEventsFromFile.class); 22 | private static final String GROUP_KEY = "EXTRACT_EVENTS"; 23 | private static final int COMMAND_TIMEOUT = 10_000; 24 | private static final int MAX_LINE_LENGTH = 1_000_000; 25 | private static final int MAX_LOG_LINES = 100; 26 | private static final String ERROR_WORD = "ERROR"; 27 | private static final String EXCEPTION_WORD = "Exception"; 28 | private static final String STACK_TRACE_PREFIX = "\tat"; 29 | private final EventRepo repo; 30 | private long lastLineRead; 31 | private final String filePath; 32 | private final String groupName; 33 | private final Pattern startPattern; 34 | private final List extractionTags; 35 | private boolean isException; 36 | 37 | protected CommandExtractEventsFromFile(EventRepo repo, long lastLineRead, String filePath, 38 | String groupName, String startPattern, List extractionTags) { 39 | super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(GROUP_KEY)) 40 | .andCommandPropertiesDefaults(HystrixCommandProperties.Setter() 41 | .withExecutionTimeoutInMilliseconds(COMMAND_TIMEOUT))); 42 | this.repo = repo; 43 | this.lastLineRead = lastLineRead; 44 | this.filePath = filePath; 45 | this.groupName = groupName; 46 | this.extractionTags = extractionTags; 47 | this.startPattern = startPattern == null ? null : Pattern.compile('^' + startPattern); 48 | } 49 | 50 | @Override 51 | protected Long run() throws Exception { 52 | log.trace("Start reading from line {} from file {}", lastLineRead, filePath); 53 | 54 | List events = new ArrayList<>(); 55 | int lineNumber = -1; 56 | 57 | // Limit to only process last 100 lines 58 | int linesInFile = countLinesInFile(filePath); 59 | if (linesInFile - lastLineRead > MAX_LOG_LINES) { 60 | lastLineRead = linesInFile - MAX_LOG_LINES; 61 | } 62 | 63 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), 64 | StandardCharsets.UTF_8))) { 65 | 66 | String line = reader.readLine(); 67 | Event event = null; 68 | 69 | while (line != null) { 70 | if (++lineNumber > lastLineRead) { 71 | 72 | if (hasStartPattern(line) || event == null) { 73 | event = new Event(lineNumber, line); 74 | if (matchLineToTags(event)) { 75 | events.add(event); 76 | } 77 | } else if (event.getLine().length() < MAX_LINE_LENGTH) { 78 | // Append to the current log event if this line is a continuation. 79 | event.setLine(event.getLine() + "\n" + line); 80 | } 81 | } 82 | line = reader.readLine(); 83 | } 84 | 85 | } catch (IOException e) { 86 | log.error("Error reading log file {}", filePath, e); 87 | } 88 | 89 | lastLineRead = lineNumber; 90 | repo.addEvents(events); 91 | 92 | log.trace("Line {} was the last line read from file {}. Number of events in repo {}", lastLineRead, filePath, events.size()); 93 | return lastLineRead; 94 | } 95 | 96 | public static int countLinesInFile(String filename) throws IOException { 97 | InputStream is = new BufferedInputStream(new FileInputStream(filename)); 98 | try { 99 | byte[] c = new byte[1024]; 100 | 101 | int readChars = is.read(c); 102 | if (readChars == -1) { 103 | // bail out if nothing to read 104 | return 0; 105 | } 106 | 107 | // make it easy for the optimizer to tune this loop 108 | int count = 0; 109 | while (readChars == 1024) { 110 | for (int i = 0; i < 1024; ) { 111 | if (c[i++] == '\n') { 112 | ++count; 113 | } 114 | } 115 | readChars = is.read(c); 116 | } 117 | 118 | // count remaining characters 119 | while (readChars != -1) { 120 | System.out.println(readChars); 121 | for (int i = 0; i < readChars; ++i) { 122 | if (c[i] == '\n') { 123 | ++count; 124 | } 125 | } 126 | readChars = is.read(c); 127 | } 128 | 129 | return count == 0 ? 1 : count; 130 | } finally { 131 | is.close(); 132 | } 133 | } 134 | 135 | private boolean hasStartPattern(String line) { 136 | return startPattern == null || startPattern.matcher(line).find(); 137 | } 138 | 139 | private boolean matchLineToTags(Event line) { 140 | if (line == null) { 141 | return false; 142 | } 143 | 144 | for (EventExtractionTag tag : extractionTags) { 145 | if (matchLineToTag(line, tag)) { 146 | return true; 147 | } 148 | } 149 | return false; 150 | } 151 | 152 | private boolean matchLineToTag(Event line, EventExtractionTag tag) { 153 | line.setGroupName(groupName); 154 | line.setFileName(filePath); 155 | String logLine = line.getLine(); 156 | 157 | if (isException) { 158 | if (logLine.startsWith(STACK_TRACE_PREFIX)) { 159 | line.setTag(EXCEPTION_WORD); 160 | return true; 161 | } 162 | isException = false; 163 | } 164 | boolean isMatch = matchAgainstRegex(tag.regex, logLine); 165 | if (isMatch) { 166 | line.setTag(tag.tagName); 167 | return true; 168 | } 169 | else if (logLine.contains(ERROR_WORD)) { 170 | line.setTag(ERROR_WORD); 171 | return true; 172 | } 173 | else if (logLine.contains(EXCEPTION_WORD)) { 174 | line.setTag(EXCEPTION_WORD); 175 | isException = true; 176 | return true; 177 | } 178 | return false; 179 | } 180 | 181 | private boolean matchAgainstRegex(String regex, String logLine) { 182 | Matcher matcher = Pattern.compile(regex).matcher(logLine); 183 | return matcher.find(); 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/main/java/no/cantara/jau/util/PropertiesHelper.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau.util; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.FileOutputStream; 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.io.OutputStream; 9 | import java.util.LinkedHashMap; 10 | import java.util.Map; 11 | import java.util.Properties; 12 | 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | 16 | public class PropertiesHelper { 17 | 18 | private static final Logger log = LoggerFactory.getLogger(PropertiesHelper.class); 19 | 20 | public static final String CONFIG_SERVICE_URL_KEY = "configservice.url"; 21 | public static final String CLIENT_NAME_PROPERTY_DEFAULT_VALUE = "Default clientName"; 22 | public static final String JAU_CONFIG_FILENAME = "jau.properties"; 23 | public static final String APPLICATION_ENV_FILENAME = "application-env.properties"; 24 | public static final String VERSION_FILENAME = "version.properties"; 25 | 26 | private static final String CLIENT_NAME_PROPERTY_KEY = "clientName"; 27 | private static final String CLIENT_ID_PROPERTY_KEY = "configservice.clientid"; 28 | private static final String ARTIFACT_ID = "configservice.artifactid"; 29 | private static final String CONFIG_SERVICE_USERNAME_KEY = "configservice.username"; 30 | private static final String CONFIG_SERVICE_PASSWORD_KEY = "configservice.password"; 31 | private static final String START_PATTERN_PROPERTY_KEY = "startPattern"; 32 | private static final String VERSION_PROPERTY_KEY = "jau.version"; 33 | private static final String IS_RUNNING_INTERVAL_KEY = "isrunninginterval"; 34 | private static final String UPDATE_INTERVAL_KEY = "updateinterval"; 35 | private static final String STOP_APPLICATION_ON_SHUTDOWN_KEY = "stopApplicationOnShutdown"; 36 | private static final String FORCE_START_SUB_PROCESS = "forceStartSubProcess"; 37 | 38 | private static final int DEFAULT_UPDATE_INTERVAL = 60; // seconds 39 | private static final int DEFAULT_IS_RUNNING_INTERVAL = 40; // seconds 40 | private static final boolean DEFAULT_STOP_APPLICATION_ON_SHUTDOWN = false; 41 | private static final boolean DEFAULT_FORCE_START_SUB_PROCESS = false; 42 | 43 | public static Properties getPropertiesFromConfigFile(String filename) { 44 | Properties properties = new Properties(); 45 | try { 46 | properties.load(PropertiesHelper.class.getClassLoader().getResourceAsStream(filename)); 47 | } catch (NullPointerException | IOException e) { 48 | log.debug("{} not found on classpath.", filename); 49 | } 50 | return properties; 51 | } 52 | 53 | public static Properties loadProperties(File file) { 54 | Properties properties = new Properties(); 55 | if (file.exists()) { 56 | try (InputStream input = new FileInputStream(file)) { 57 | properties.load(input); 58 | } catch (Exception e) { 59 | log.warn("Failed to load properties from {}", file, e); 60 | } 61 | } 62 | return properties; 63 | } 64 | 65 | public static boolean saveProperties(Properties properties, File file) { 66 | try (OutputStream output = new FileOutputStream(file)) { 67 | properties.store(output, null); 68 | return true; 69 | } catch (Exception e) { 70 | log.warn("Failed to save properties to {}", file, e); 71 | return false; 72 | } 73 | } 74 | 75 | public static String getStringProperty(final Properties properties, String propertyKey, String defaultValue) { 76 | String property = System.getProperty(propertyKey); 77 | if (property == null) { 78 | property = AppConfig.getString(propertyKey); 79 | } 80 | if (property == null) { 81 | property = properties.getProperty(propertyKey, defaultValue); 82 | } 83 | return property; 84 | } 85 | 86 | public static Integer getIntProperty(final Properties properties, String propertyKey, Integer defaultValue) { 87 | String property = getStringProperty(properties, propertyKey, null); 88 | if (property == null) { 89 | return defaultValue; 90 | } 91 | return Integer.valueOf(property); 92 | } 93 | 94 | public static Long getLongProperty(final Properties properties, String propertyKey, Long defaultValue) { 95 | String property = getStringProperty(properties, propertyKey, null); 96 | if (property == null) { 97 | return defaultValue; 98 | } 99 | return Long.valueOf(property); 100 | } 101 | 102 | public static void setLongProperty(Properties properties, String propertyKey, long value) { 103 | properties.setProperty(propertyKey, String.valueOf(value)); 104 | } 105 | 106 | public static Boolean getBooleanProperty(final Properties properties, String propertyKey, Boolean defaultValue) { 107 | String property = getStringProperty(properties, propertyKey, null); 108 | if (property == null) { 109 | return defaultValue; 110 | } 111 | return Boolean.valueOf(property); 112 | } 113 | 114 | public static Map propertiesAsMap(Properties properties) { 115 | Map map = new LinkedHashMap<>(); 116 | properties.forEach((key, value) -> map.put(String.valueOf(key), String.valueOf(value))); 117 | return map; 118 | } 119 | 120 | public static String getClientId() { 121 | return getStringProperty(getPropertiesFromConfigFile(JAU_CONFIG_FILENAME), CLIENT_ID_PROPERTY_KEY, null); 122 | } 123 | 124 | public static String getClientName() { 125 | return getStringProperty(getPropertiesFromConfigFile(JAU_CONFIG_FILENAME), CLIENT_NAME_PROPERTY_KEY, CLIENT_NAME_PROPERTY_DEFAULT_VALUE); 126 | } 127 | 128 | public static String getArtifactId() { 129 | return getStringProperty(getPropertiesFromConfigFile(JAU_CONFIG_FILENAME), ARTIFACT_ID, null); 130 | } 131 | 132 | public static String getConfigServiceUrl() { 133 | return getStringProperty(getPropertiesFromConfigFile(JAU_CONFIG_FILENAME), CONFIG_SERVICE_URL_KEY, null); 134 | } 135 | 136 | public static String getUsername() { 137 | return getStringProperty(getPropertiesFromConfigFile(JAU_CONFIG_FILENAME), CONFIG_SERVICE_USERNAME_KEY, null); 138 | } 139 | 140 | public static String getPassword() { 141 | return getStringProperty(getPropertiesFromConfigFile(JAU_CONFIG_FILENAME), CONFIG_SERVICE_PASSWORD_KEY, null); 142 | } 143 | 144 | public static int getUpdateInterval() { 145 | return getIntProperty(getPropertiesFromConfigFile(JAU_CONFIG_FILENAME), UPDATE_INTERVAL_KEY, DEFAULT_UPDATE_INTERVAL); 146 | } 147 | 148 | public static int getIsRunningInterval() { 149 | return getIntProperty(getPropertiesFromConfigFile(JAU_CONFIG_FILENAME), IS_RUNNING_INTERVAL_KEY, DEFAULT_IS_RUNNING_INTERVAL); 150 | } 151 | 152 | public static String getVersion() { 153 | return getStringProperty(getPropertiesFromConfigFile(VERSION_FILENAME), VERSION_PROPERTY_KEY, null); 154 | } 155 | 156 | public static String getStartPattern() { 157 | return getStringProperty(getPropertiesFromConfigFile(JAU_CONFIG_FILENAME), START_PATTERN_PROPERTY_KEY, null); 158 | } 159 | 160 | public static boolean stopApplicationOnShutdown() { 161 | return getBooleanProperty(getPropertiesFromConfigFile(JAU_CONFIG_FILENAME), STOP_APPLICATION_ON_SHUTDOWN_KEY, DEFAULT_STOP_APPLICATION_ON_SHUTDOWN); 162 | } 163 | 164 | public static boolean forceStartSubProcess() { 165 | return getBooleanProperty(getPropertiesFromConfigFile(JAU_CONFIG_FILENAME), FORCE_START_SUB_PROCESS, DEFAULT_FORCE_START_SUB_PROCESS); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/main/java/no/cantara/jau/JavaAutoUpdater.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau; 2 | 3 | import no.cantara.cs.client.ConfigServiceClient; 4 | import no.cantara.cs.client.ConfigurationStoreUtil; 5 | import no.cantara.cs.client.DownloadUtil; 6 | import no.cantara.cs.dto.ApplicationConfig; 7 | import no.cantara.cs.dto.ClientConfig; 8 | import no.cantara.jau.coms.CheckForUpdateHelper; 9 | import no.cantara.jau.coms.RegisterClientHelper; 10 | import no.cantara.jau.duplicatehandler.DuplicateProcessHandler; 11 | import no.cantara.jau.eventextraction.EventExtractorService; 12 | import no.cantara.jau.util.PropertiesHelper; 13 | 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | import java.util.Properties; 18 | import java.util.concurrent.Executors; 19 | import java.util.concurrent.ScheduledExecutorService; 20 | import java.util.concurrent.ScheduledFuture; 21 | 22 | import static java.util.concurrent.TimeUnit.SECONDS; 23 | 24 | public class JavaAutoUpdater { 25 | 26 | private static final Logger log = LoggerFactory.getLogger(JavaAutoUpdater.class); 27 | 28 | private static ScheduledFuture processMonitorHandle; 29 | private static ScheduledFuture updaterHandle; 30 | 31 | private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); 32 | private final ConfigServiceClient configServiceClient; 33 | private final ApplicationProcess processHolder; 34 | private final DuplicateProcessHandler duplicateProcessHandler; 35 | private final EventExtractorService extractorService; 36 | 37 | private RegisterClientHelper registerClientHelper; 38 | 39 | public JavaAutoUpdater(ConfigServiceClient configServiceClient, RegisterClientHelper registerClientHelper, 40 | ApplicationProcess applicationProcess, DuplicateProcessHandler duplicateProcessHandler, 41 | EventExtractorService extractorService) { 42 | this.configServiceClient = configServiceClient; 43 | this.registerClientHelper = registerClientHelper; 44 | this.processHolder = applicationProcess; 45 | this.duplicateProcessHandler = duplicateProcessHandler; 46 | this.extractorService = extractorService; 47 | 48 | addShutdownHook(); 49 | } 50 | 51 | /** 52 | * Registers a shutdown hook that attempts to stop the application process when JAU is stopped. 53 | */ 54 | private void addShutdownHook() { 55 | if (PropertiesHelper.stopApplicationOnShutdown()) { 56 | Runtime.getRuntime().addShutdownHook(new Thread() { 57 | @Override 58 | public void run() { 59 | processHolder.stopProcess(); 60 | } 61 | }); 62 | log.info("Registered shutdown hook for stopping application."); 63 | } 64 | } 65 | 66 | /** 67 | * registerClient 68 | * checkForUpdate 69 | * if changed 70 | * Download 71 | * Stop existing service if running 72 | * Start new service 73 | */ 74 | public void start(int updateInterval, int isRunningInterval) { 75 | // registerClient or fetch applicationState from file 76 | if (configServiceClient.getApplicationState() == null) { 77 | ClientConfig clientConfig = registerClient(); 78 | storeClientFiles(clientConfig); 79 | } else { 80 | log.debug("Client already registered. Skip registerClient and use properties from file."); 81 | } 82 | 83 | Properties initialApplicationState = configServiceClient.getApplicationState(); 84 | initializeProcessHolder(initialApplicationState); 85 | extractorService.updateConfigs(configServiceClient.getEventExtractionConfigs()); 86 | 87 | // checkForUpdate and start process 88 | while (true) { 89 | int sleepTime = 10000; 90 | if (updaterHandle == null || updaterHandle.isCancelled() || updaterHandle.isDone()) { 91 | updaterHandle = startUpdaterThread(updateInterval); 92 | } 93 | 94 | if (processMonitorHandle == null || processMonitorHandle.isCancelled() || processMonitorHandle.isDone()) { 95 | String processCommand = configServiceClient.getApplicationState().getProperty("command"); 96 | boolean successKillingProcess = duplicateProcessHandler.killExistingProcessIfRunning(processCommand); 97 | 98 | if (!successKillingProcess) { 99 | boolean forceStartSubProcess = PropertiesHelper.forceStartSubProcess(); 100 | if (forceStartSubProcess) { 101 | log.warn("Problem killing running process. Will start a new managed process as the " + 102 | "flag 'forceStartSubProcess' is set to true. This may lead to duplicate " + 103 | "subprocesses."); 104 | } else { 105 | log.error("Problem killing running process! A new managed process will not be started. " + 106 | "Retrying in {} seconds", sleepTime / 1000); 107 | } 108 | } else { 109 | processMonitorHandle = startProcessMonitorThread(isRunningInterval); 110 | } 111 | } 112 | 113 | // make sure everything runs, forever 114 | try { 115 | Thread.sleep(sleepTime); 116 | } catch (InterruptedException e) { 117 | log.warn("Thread was interrupted", e); 118 | } 119 | } 120 | } 121 | 122 | private ScheduledFuture startProcessMonitorThread(long interval) { 123 | log.debug("Starting process monitoring scheduler with an update interval of {} seconds.", interval); 124 | return scheduler.scheduleAtFixedRate( 125 | () -> { 126 | log.debug("Checking if process is running..."); 127 | 128 | // Restart, whatever the reason the process is not running. 129 | if (!processHolder.processIsRunning()) { 130 | log.debug("Process is not running - restarting... clientId={}, lastChanged={}, command={}", 131 | processHolder.getClientId(), processHolder.getLastChangedTimestamp(), processHolder.getCommand()); 132 | 133 | processHolder.startProcess(); 134 | } 135 | }, 136 | 1, interval, SECONDS 137 | ); 138 | } 139 | 140 | private ScheduledFuture startUpdaterThread(long interval) { 141 | log.debug("Starting update scheduler with an update interval of {} seconds.", interval); 142 | return scheduler.scheduleWithFixedDelay( 143 | CheckForUpdateHelper.getCheckForUpdateRunnable(interval, configServiceClient, processHolder, 144 | processMonitorHandle, extractorService, this), 145 | 1, interval, SECONDS 146 | ); 147 | } 148 | 149 | public ClientConfig registerClient() { 150 | return registerClientHelper.registerClient(); 151 | } 152 | 153 | public void storeClientFiles(ClientConfig clientConfig) { 154 | String workingDirectory = processHolder.getWorkingDirectory().getAbsolutePath(); 155 | ApplicationConfig config = clientConfig.config; 156 | DownloadUtil.downloadAllFiles(config.getDownloadItems(), workingDirectory); 157 | ConfigurationStoreUtil.toFiles(config.getConfigurationStores(), workingDirectory); 158 | extractorService.updateConfigs(config.getEventExtractionConfigs()); 159 | } 160 | 161 | private void initializeProcessHolder(Properties initialApplicationState) { 162 | String initialClientId = PropertiesHelper.getStringProperty(initialApplicationState, ConfigServiceClient.CLIENT_ID, null); 163 | String initialLastChanged = PropertiesHelper.getStringProperty(initialApplicationState, ConfigServiceClient.LAST_CHANGED, null); 164 | String initialCommand = PropertiesHelper.getStringProperty(initialApplicationState, ConfigServiceClient.COMMAND, null); 165 | Properties environment = PropertiesHelper.getPropertiesFromConfigFile(PropertiesHelper.APPLICATION_ENV_FILENAME); 166 | 167 | processHolder.setCommand(initialCommand.split("\\s+")); 168 | processHolder.setClientId(initialClientId); 169 | processHolder.setLastChangedTimestamp(initialLastChanged); 170 | processHolder.setEnvironment(PropertiesHelper.propertiesAsMap(environment)); 171 | } 172 | 173 | } 174 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /src/test/java/no/cantara/jau/duplicatehandler/DuplicateProcessHandlerTest.java: -------------------------------------------------------------------------------- 1 | package no.cantara.jau.duplicatehandler; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.testng.Assert; 6 | import org.testng.annotations.Test; 7 | 8 | import java.io.IOException; 9 | 10 | import static org.mockito.Mockito.*; 11 | 12 | public class DuplicateProcessHandlerTest { 13 | private static final Logger log = LoggerFactory.getLogger(DuplicateProcessHandlerTest.class); 14 | 15 | @Test 16 | public void shouldFindProcessIdAndWriteToFile() throws ReflectiveOperationException { 17 | ProcessExecutorFetcher processExecutorFetcher = mock(ProcessExecutorFetcher.class); 18 | ProcessExecutor processExecutorMock = mock(ProcessExecutor.class); 19 | when(processExecutorFetcher.getProcessExecutorBasedOnOs()).thenReturn(processExecutorMock); 20 | LastRunningProcessFileUtil lastRunningProcessFileUtilMock = mock(LastRunningProcessFileUtil.class); 21 | 22 | when(processExecutorMock.findProcessId(any())).thenReturn("12345"); 23 | 24 | DuplicateProcessHandler duplicateProcessHandler = new DuplicateProcessHandler(processExecutorFetcher, 25 | lastRunningProcessFileUtilMock); 26 | duplicateProcessHandler.findRunningManagedProcessPidAndWriteToFile(any(Process.class)); 27 | 28 | verify(lastRunningProcessFileUtilMock, times(1)).writePidToFile("12345"); 29 | } 30 | 31 | @Test 32 | public void shouldReturnTrueWhenProcessIsKilled() throws IOException, InterruptedException { 33 | ProcessExecutorFetcher processExecutorFetcher = mock(ProcessExecutorFetcher.class); 34 | ProcessExecutor processExecutorMock = mock(ProcessExecutor.class); 35 | when(processExecutorFetcher.getProcessExecutorBasedOnOs()).thenReturn(processExecutorMock); 36 | LastRunningProcessFileUtil lastRunningProcessFileUtilMock = mock(LastRunningProcessFileUtil.class); 37 | 38 | when(lastRunningProcessFileUtilMock.getRunningProcessPidFromFile()).thenReturn("12345"); 39 | when(processExecutorMock.isProcessRunning("12345")).thenReturn(true); 40 | when(processExecutorMock.killProcessByPID("12345")).thenReturn(true); 41 | 42 | DuplicateProcessHandler duplicateProcessHandler = new DuplicateProcessHandler(processExecutorFetcher, 43 | lastRunningProcessFileUtilMock); 44 | boolean result = duplicateProcessHandler.killExistingProcessIfRunning("processCommand"); 45 | 46 | Assert.assertTrue(result); 47 | } 48 | 49 | @Test 50 | public void shouldReturnFalseWhenKillingByPidFailsAndFallbackToKillByProcessNameFails() throws IOException, InterruptedException { 51 | ProcessExecutorFetcher processExecutorFetcher = mock(ProcessExecutorFetcher.class); 52 | ProcessExecutor processExecutorMock = mock(ProcessExecutor.class); 53 | when(processExecutorFetcher.getProcessExecutorBasedOnOs()).thenReturn(processExecutorMock); 54 | LastRunningProcessFileUtil lastRunningProcessFileUtilMock = mock(LastRunningProcessFileUtil.class); 55 | 56 | when(lastRunningProcessFileUtilMock.getRunningProcessPidFromFile()).thenReturn("12345"); 57 | when(processExecutorMock.isProcessRunning("12345")).thenReturn(true); 58 | when(processExecutorMock.killProcessByPID("12345")).thenReturn(false); 59 | when(processExecutorMock.killProcessByProcessName(anyString())).thenReturn(false); 60 | 61 | DuplicateProcessHandler duplicateProcessHandler = new DuplicateProcessHandler(processExecutorFetcher, 62 | lastRunningProcessFileUtilMock); 63 | boolean result = duplicateProcessHandler.killExistingProcessIfRunning("java -jar java_application-123.alpha-2.jar"); 64 | 65 | Assert.assertFalse(result); 66 | } 67 | 68 | @Test 69 | public void shouldFallbackToKillingByProcessNameWhenErrorReadingPidFile() throws IOException, InterruptedException { 70 | ProcessExecutorFetcher processExecutorFetcher = mock(ProcessExecutorFetcher.class); 71 | ProcessExecutor processExecutorMock = mock(ProcessExecutor.class); 72 | when(processExecutorFetcher.getProcessExecutorBasedOnOs()).thenReturn(processExecutorMock); 73 | LastRunningProcessFileUtil lastRunningProcessFileUtilMock = mock(LastRunningProcessFileUtil.class); 74 | 75 | doThrow(new IOException()).when(lastRunningProcessFileUtilMock).getRunningProcessPidFromFile(); 76 | when(processExecutorMock.killProcessByProcessName("java_application-123.alpha-2.jar")).thenReturn(true); 77 | 78 | DuplicateProcessHandler duplicateProcessHandler = new DuplicateProcessHandler(processExecutorFetcher, 79 | lastRunningProcessFileUtilMock); 80 | boolean result = duplicateProcessHandler.killExistingProcessIfRunning("java -jar java_application-123.alpha-2.jar"); 81 | 82 | verify(processExecutorMock, times(1)).killProcessByProcessName("java_application-123.alpha-2.jar"); 83 | Assert.assertTrue(result); 84 | } 85 | 86 | @Test 87 | public void shouldReturnTrueWhenProcessIsNotRunning() throws IOException, InterruptedException { 88 | ProcessExecutorFetcher processExecutorFetcher = mock(ProcessExecutorFetcher.class); 89 | ProcessExecutor processExecutorMock = mock(ProcessExecutor.class); 90 | when(processExecutorFetcher.getProcessExecutorBasedOnOs()).thenReturn(processExecutorMock); 91 | LastRunningProcessFileUtil lastRunningProcessFileUtilMock = mock(LastRunningProcessFileUtil.class); 92 | 93 | when(lastRunningProcessFileUtilMock.getRunningProcessPidFromFile()).thenReturn("12345"); 94 | when(processExecutorMock.isProcessRunning("12345")).thenReturn(false); 95 | 96 | DuplicateProcessHandler duplicateProcessHandler = new DuplicateProcessHandler(processExecutorFetcher, 97 | lastRunningProcessFileUtilMock); 98 | boolean result = duplicateProcessHandler.killExistingProcessIfRunning("processCommand"); 99 | 100 | Assert.assertTrue(result); 101 | } 102 | 103 | @Test 104 | public void shouldReturnTrueWhenPIDFileDoesNotExist() throws IOException { 105 | ProcessExecutorFetcher processExecutorFetcher = mock(ProcessExecutorFetcher.class); 106 | ProcessExecutor processExecutorMock = mock(ProcessExecutor.class); 107 | when(processExecutorFetcher.getProcessExecutorBasedOnOs()).thenReturn(processExecutorMock); 108 | LastRunningProcessFileUtil lastRunningProcessFileUtilMock = mock(LastRunningProcessFileUtil.class); 109 | 110 | when(lastRunningProcessFileUtilMock.getRunningProcessPidFromFile()).thenReturn(null); 111 | 112 | DuplicateProcessHandler duplicateProcessHandler = new DuplicateProcessHandler(processExecutorFetcher, 113 | lastRunningProcessFileUtilMock); 114 | boolean result = duplicateProcessHandler.killExistingProcessIfRunning("processCommand"); 115 | 116 | Assert.assertTrue(result); 117 | } 118 | 119 | @Test 120 | public void shouldReturnFalseWhenReadingOfPIDFileThrowsException() throws IOException { 121 | ProcessExecutorFetcher processExecutorFetcher = mock(ProcessExecutorFetcher.class); 122 | ProcessExecutor processExecutorMock = mock(ProcessExecutor.class); 123 | when(processExecutorFetcher.getProcessExecutorBasedOnOs()).thenReturn(processExecutorMock); 124 | LastRunningProcessFileUtil lastRunningProcessFileUtilMock = mock(LastRunningProcessFileUtil.class); 125 | 126 | when(lastRunningProcessFileUtilMock.getRunningProcessPidFromFile()).thenThrow(new IOException()); 127 | 128 | DuplicateProcessHandler duplicateProcessHandler = new DuplicateProcessHandler(processExecutorFetcher, 129 | lastRunningProcessFileUtilMock); 130 | boolean result = duplicateProcessHandler.killExistingProcessIfRunning("processCommand"); 131 | 132 | Assert.assertFalse(result); 133 | } 134 | 135 | @Test 136 | public void shouldReturnFalseWhenPIDIsInvalid() throws IOException { 137 | ProcessExecutorFetcher processExecutorFetcher = mock(ProcessExecutorFetcher.class); 138 | ProcessExecutor processExecutorMock = mock(ProcessExecutor.class); 139 | when(processExecutorFetcher.getProcessExecutorBasedOnOs()).thenReturn(processExecutorMock); 140 | LastRunningProcessFileUtil lastRunningProcessFileUtilMock = mock(LastRunningProcessFileUtil.class); 141 | 142 | when(lastRunningProcessFileUtilMock.getRunningProcessPidFromFile()).thenReturn("blablainvalid"); 143 | 144 | DuplicateProcessHandler duplicateProcessHandler = new DuplicateProcessHandler(processExecutorFetcher, 145 | lastRunningProcessFileUtilMock); 146 | boolean result = duplicateProcessHandler.killExistingProcessIfRunning("processCommand"); 147 | 148 | Assert.assertFalse(result); 149 | } 150 | 151 | @Test 152 | public void shouldReturnFalseWhenCannotDetectIfProcessIsRunning() throws IOException, InterruptedException { 153 | ProcessExecutorFetcher processExecutorFetcher = mock(ProcessExecutorFetcher.class); 154 | ProcessExecutor processExecutorMock = mock(ProcessExecutor.class); 155 | when(processExecutorFetcher.getProcessExecutorBasedOnOs()).thenReturn(processExecutorMock); 156 | LastRunningProcessFileUtil lastRunningProcessFileUtilMock = mock(LastRunningProcessFileUtil.class); 157 | 158 | when(lastRunningProcessFileUtilMock.getRunningProcessPidFromFile()).thenReturn("13245"); 159 | when(processExecutorMock.isProcessRunning("13245")).thenThrow(new IOException()); 160 | 161 | DuplicateProcessHandler duplicateProcessHandler = new DuplicateProcessHandler(processExecutorFetcher, 162 | lastRunningProcessFileUtilMock); 163 | boolean result = duplicateProcessHandler.killExistingProcessIfRunning("processCommand"); 164 | 165 | Assert.assertFalse(result); 166 | } 167 | 168 | @Test 169 | public void shouldReturnFalseWhenProcessCannotBeKilledByPidOrName() throws IOException, InterruptedException { 170 | ProcessExecutorFetcher processExecutorFetcher = mock(ProcessExecutorFetcher.class); 171 | ProcessExecutor processExecutorMock = mock(ProcessExecutor.class); 172 | when(processExecutorFetcher.getProcessExecutorBasedOnOs()).thenReturn(processExecutorMock); 173 | LastRunningProcessFileUtil lastRunningProcessFileUtilMock = mock(LastRunningProcessFileUtil.class); 174 | 175 | when(lastRunningProcessFileUtilMock.getRunningProcessPidFromFile()).thenReturn("13245"); 176 | when(processExecutorMock.isProcessRunning("13245")).thenReturn(true); 177 | doThrow(new IOException()).when(processExecutorMock).killProcessByPID("13245"); 178 | 179 | DuplicateProcessHandler duplicateProcessHandler = new DuplicateProcessHandler(processExecutorFetcher, 180 | lastRunningProcessFileUtilMock); 181 | boolean result = duplicateProcessHandler.killExistingProcessIfRunning("processCommand"); 182 | 183 | Assert.assertFalse(result); 184 | } 185 | 186 | @Test 187 | public void shouldExtractProcessNameFromCommand() throws IOException, InterruptedException { 188 | ProcessExecutorFetcher processExecutorFetcher = mock(ProcessExecutorFetcher.class); 189 | ProcessExecutor processExecutorMock = mock(ProcessExecutor.class); 190 | when(processExecutorFetcher.getProcessExecutorBasedOnOs()).thenReturn(processExecutorMock); 191 | LastRunningProcessFileUtil lastRunningProcessFileUtilMock = mock(LastRunningProcessFileUtil.class); 192 | 193 | when(lastRunningProcessFileUtilMock.getRunningProcessPidFromFile()).thenReturn("13245"); 194 | when(processExecutorMock.isProcessRunning("13245")).thenReturn(true); 195 | doThrow(new IOException()).when(processExecutorMock).killProcessByPID("13245"); 196 | 197 | DuplicateProcessHandler duplicateProcessHandler = new DuplicateProcessHandler(processExecutorFetcher, 198 | lastRunningProcessFileUtilMock); 199 | 200 | String processName = duplicateProcessHandler.extractProcessNameFromCommand("java -jar pharmacy-agent-0.8-SNAPSHOT.jar"); 201 | 202 | Assert.assertEquals(processName, "pharmacy-agent-0.8-SNAPSHOT.jar"); 203 | } 204 | 205 | } 206 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | no.cantara.jau 4 | java-auto-update 5 | 0.18.32-SNAPSHOT 6 | Java Auto-Update 7 | https://wiki.cantara.no/display/dev/Java+Auto-Update 8 | 9 | 10 | 11 | 12 | no.cantara.emi 13 | parent 14 | 8 15 | 16 | 17 | 18 | scm:git:ssh://git@github.com/cantara/Java-Auto-Update.git 19 | scm:git:https://github.com/cantara/Java-Auto-Update.git 20 | https://github.com/Cantara/Java-Auto-Update 21 | java-auto-update-0.18.3 22 | 23 | 24 | 25 | ${project.artifactId} 26 | 1.7.36 27 | 28 | 29 | 30 | 31 | 32 | 33 | no.cantara.jau 34 | configservice-sdk 35 | 0.20.20 36 | 37 | 38 | 39 | com.netflix.hystrix 40 | hystrix-core 41 | 1.5.18 42 | 43 | 44 | commons-logging 45 | commons-logging 46 | 47 | 48 | 49 | 50 | io.reactivex 51 | rxjava 52 | 1.3.8 53 | 54 | 55 | 56 | org.glassfish.jersey.core 57 | jersey-common 58 | 2.47 59 | 60 | 61 | 62 | 63 | net.java.dev.jna 64 | jna-platform 65 | 5.18.1 66 | 67 | 68 | 69 | org.slf4j 70 | slf4j-api 71 | ${slf4j.version} 72 | 73 | 74 | 75 | org.slf4j 76 | jcl-over-slf4j 77 | 1.7.36 78 | runtime 79 | 80 | 81 | ch.qos.logback 82 | logback-classic 83 | 1.5.22 84 | runtime 85 | 86 | 87 | 88 | janino 89 | janino 90 | 2.5.10 91 | 92 | 93 | 94 | 95 | org.testng 96 | testng 97 | 7.11.0 98 | test 99 | 100 | 101 | org.mockito 102 | mockito-core 103 | 4.11.0 104 | test 105 | 106 | 107 | org.constretto 108 | constretto-core 109 | 2.2.3 110 | 111 | 112 | 113 | 114 | 115 | 116 | src/main/resources 117 | true 118 | 119 | 120 | 121 | 122 | 123 | 124 | org.codehaus.mojo 125 | appassembler-maven-plugin 126 | 2.1.0 127 | 128 | flat 129 | false 130 | true 131 | false 132 | environment-setup 133 | ${project.build.directory} 134 | 135 | 136 | ${project.artifactId} 137 | no.cantara.jau.Main 138 | 139 | jsw 140 | 141 | 142 | 143 | jsw 144 | 145 | windows-x86-32 146 | windows-x86-64 147 | 148 | 149 | 150 | configuration.directory.in.classpath.first 151 | config_override 152 | 153 | 154 | set.default.REPO_DIR 155 | lib 156 | 157 | 158 | wrapper.logfile 159 | wrapper.log 160 | 161 | 162 | wrapper.logfile.maxsize 163 | 1m 164 | 165 | 166 | wrapper.logfile.maxfiles 167 | 1 168 | 169 | 170 | wrapper.logfile.loglevel 171 | INFO 172 | 173 | 174 | 175 | 176 | wrapper.java.command 177 | java/bin/java 178 | 179 | 180 | 181 | wrapper.console.title 182 | ${service.name} 183 | 184 | 185 | wrapper.ntservice.name 186 | ${service.name} 187 | 188 | 189 | wrapper.ntservice.displayname 190 | ${service.name} 191 | 192 | 193 | wrapper.ntservice.starttype 194 | AUTO_START 195 | 196 | 197 | 198 | 199 | 200 | 16M 201 | 128M 202 | 203 | -server 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | generate-jsw-scripts 213 | package 214 | 215 | generate-daemons 216 | 217 | 218 | 219 | 220 | 221 | 222 | org.apache.maven.plugins 223 | maven-shade-plugin 224 | 225 | 226 | 227 | *:* 228 | 229 | META-INF/*.SF 230 | META-INF/*.DSA 231 | META-INF/*.RSA 232 | 233 | 234 | 235 | 236 | 237 | 238 | package 239 | 240 | shade 241 | 242 | 243 | 244 | 245 | *:* 246 | 247 | META-INF/*.SF 248 | META-INF/*.DSA 249 | META-INF/*.RSA 250 | 251 | 252 | 253 | 254 | 255 | 256 | no.cantara.jau.Main 257 | . ./config_override 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | maven-assembly-plugin 268 | 269 | 270 | src/main/assembly/assembly.xml 271 | 272 | false 273 | 274 | 275 | 276 | assemble-zip 277 | package 278 | 279 | single 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | Erik Drolshammer 290 | erik-dev@fjas.no 291 | 292 | Developer 293 | 294 | 295 | 296 | Jørund B. Fagerjord 297 | j.b.fagerjord@gmail.com 298 | 299 | Developer 300 | 301 | 302 | 303 | Anders Emil Rønning 304 | anders.roenning@gmail.com 305 | 306 | Developer 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | cantara-releases 315 | Cantara Release Repository 316 | https://mvnrepo.cantara.no/content/repositories/releases/ 317 | 318 | 319 | 320 | cantara-snapshots 321 | Cantara Snapshot Repository 322 | https://mvnrepo.cantara.no/content/repositories/snapshots/ 323 | 324 | 325 | 326 | 327 | 328 | --------------------------------------------------------------------------------