aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorStephane Gamard <stephane.gamard@searchbox.com>2014-08-08 11:27:03 +0200
committerStephane Gamard <stephane.gamard@searchbox.com>2014-08-08 12:05:12 +0200
commit17a40828946cf3c133b3d8d3ed441c4697bd3229 (patch)
tree4863d984248e420c3dd060264eef3549eaf8e413 /server
parenta9611c345fd6ad51c37f9470b1b08fea0e062946 (diff)
downloadsonarqube-17a40828946cf3c133b3d8d3ed441c4697bd3229.tar.gz
sonarqube-17a40828946cf3c133b3d8d3ed441c4697bd3229.zip
SONAR-4898 - Modularized sonar-process for tests
Diffstat (limited to 'server')
-rw-r--r--server/pom.xml3
-rw-r--r--server/process/pom.xml22
-rw-r--r--server/process/sonar-dummy-app/pom.xml (renamed from server/sonar-process-test/pom.xml)11
-rw-r--r--server/process/sonar-dummy-app/src/main/java/org/sonar/application/DummyOkApp.java (renamed from server/sonar-process-test/src/main/java/org/sonar/application/DummyOkApp.java)0
-rw-r--r--server/process/sonar-dummy-app/src/main/resources/org/sonar/application/logback.xml (renamed from server/sonar-process-test/src/main/resources/org/sonar/application/logback.xml)0
-rw-r--r--server/process/sonar-process-test/pom.xml (renamed from server/sonar-process/pom.xml)22
-rw-r--r--server/process/sonar-process-test/src/main/java/org/sonar/process/AesCipher.java (renamed from server/sonar-process/src/main/java/org/sonar/process/AesCipher.java)0
-rw-r--r--server/process/sonar-process-test/src/main/java/org/sonar/process/Base64Cipher.java (renamed from server/sonar-process/src/main/java/org/sonar/process/Base64Cipher.java)0
-rw-r--r--server/process/sonar-process-test/src/main/java/org/sonar/process/Cipher.java (renamed from server/sonar-process/src/main/java/org/sonar/process/Cipher.java)0
-rw-r--r--server/process/sonar-process-test/src/main/java/org/sonar/process/ConfigurationUtils.java (renamed from server/sonar-process/src/main/java/org/sonar/process/ConfigurationUtils.java)0
-rw-r--r--server/process/sonar-process-test/src/main/java/org/sonar/process/Encryption.java (renamed from server/sonar-process/src/main/java/org/sonar/process/Encryption.java)0
-rw-r--r--server/process/sonar-process-test/src/main/java/org/sonar/process/JmxUtils.java (renamed from server/sonar-process/src/main/java/org/sonar/process/JmxUtils.java)0
-rw-r--r--server/process/sonar-process-test/src/main/java/org/sonar/process/MessageException.java (renamed from server/sonar-process/src/main/java/org/sonar/process/MessageException.java)0
-rw-r--r--server/process/sonar-process-test/src/main/java/org/sonar/process/MinimumViableSystem.java (renamed from server/sonar-process/src/main/java/org/sonar/process/MinimumViableSystem.java)0
-rw-r--r--server/process/sonar-process-test/src/main/java/org/sonar/process/Monitor.java (renamed from server/sonar-process/src/main/java/org/sonar/process/Monitor.java)0
-rw-r--r--server/process/sonar-process-test/src/main/java/org/sonar/process/MonitoredProcess.java (renamed from server/sonar-process/src/main/java/org/sonar/process/MonitoredProcess.java)0
-rw-r--r--server/process/sonar-process-test/src/main/java/org/sonar/process/NetworkUtils.java (renamed from server/sonar-process/src/main/java/org/sonar/process/NetworkUtils.java)0
-rw-r--r--server/process/sonar-process-test/src/main/java/org/sonar/process/ProcessLogging.java (renamed from server/sonar-process/src/main/java/org/sonar/process/ProcessLogging.java)0
-rw-r--r--server/process/sonar-process-test/src/main/java/org/sonar/process/ProcessMXBean.java (renamed from server/sonar-process/src/main/java/org/sonar/process/ProcessMXBean.java)0
-rw-r--r--server/process/sonar-process-test/src/main/java/org/sonar/process/ProcessUtils.java (renamed from server/sonar-process/src/main/java/org/sonar/process/ProcessUtils.java)0
-rw-r--r--server/process/sonar-process-test/src/main/java/org/sonar/process/ProcessWrapper.java (renamed from server/sonar-process/src/main/java/org/sonar/process/ProcessWrapper.java)0
-rw-r--r--server/process/sonar-process-test/src/main/java/org/sonar/process/Props.java (renamed from server/sonar-process/src/main/java/org/sonar/process/Props.java)0
-rw-r--r--server/process/sonar-process-test/src/main/java/org/sonar/process/Terminable.java (renamed from server/sonar-process/src/main/java/org/sonar/process/Terminable.java)0
-rw-r--r--server/process/sonar-process-test/src/test/java/org/sonar/process/AesCipherTest.java (renamed from server/sonar-process/src/test/java/org/sonar/process/AesCipherTest.java)0
-rw-r--r--server/process/sonar-process-test/src/test/java/org/sonar/process/ConfigurationUtilsTest.java (renamed from server/sonar-process/src/test/java/org/sonar/process/ConfigurationUtilsTest.java)0
-rw-r--r--server/process/sonar-process-test/src/test/java/org/sonar/process/EncryptionTest.java (renamed from server/sonar-process/src/test/java/org/sonar/process/EncryptionTest.java)0
-rw-r--r--server/process/sonar-process-test/src/test/java/org/sonar/process/MinimumViableSystemTest.java (renamed from server/sonar-process/src/test/java/org/sonar/process/MinimumViableSystemTest.java)0
-rw-r--r--server/process/sonar-process-test/src/test/java/org/sonar/process/NetworkUtilsTest.java (renamed from server/sonar-process/src/test/java/org/sonar/process/NetworkUtilsTest.java)0
-rw-r--r--server/process/sonar-process-test/src/test/java/org/sonar/process/ProcessWrapperTest.java (renamed from server/sonar-process/src/test/java/org/sonar/process/ProcessWrapperTest.java)0
-rw-r--r--server/process/sonar-process-test/src/test/java/org/sonar/process/PropsTest.java (renamed from server/sonar-process/src/test/java/org/sonar/process/PropsTest.java)0
-rw-r--r--server/process/sonar-process-test/src/test/resources/org/sonar/process/AesCipherTest/aes_secret_key.txt (renamed from server/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/aes_secret_key.txt)0
-rw-r--r--server/process/sonar-process-test/src/test/resources/org/sonar/process/AesCipherTest/bad_secret_key.txt (renamed from server/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/bad_secret_key.txt)0
-rw-r--r--server/process/sonar-process-test/src/test/resources/org/sonar/process/AesCipherTest/non_trimmed_secret_key.txt (renamed from server/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/non_trimmed_secret_key.txt)0
-rw-r--r--server/process/sonar-process-test/src/test/resources/org/sonar/process/AesCipherTest/other_secret_key.txt (renamed from server/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/other_secret_key.txt)0
-rw-r--r--server/process/sonar-process-test/src/test/resources/org/sonar/process/LoggingTest/logback-access.xml (renamed from server/sonar-process/src/test/resources/org/sonar/process/LoggingTest/logback-access.xml)0
-rw-r--r--server/process/sonar-process-test/src/test/resources/org/sonar/process/ProcessTest/sonar.properties (renamed from server/sonar-process/src/test/resources/org/sonar/process/ProcessTest/sonar.properties)0
-rw-r--r--server/process/sonar-process-test/src/test/resources/org/sonar/process/PropsTest/sonar.properties (renamed from server/sonar-process/src/test/resources/org/sonar/process/PropsTest/sonar.properties)0
-rw-r--r--server/process/sonar-process/pom.xml72
-rw-r--r--server/process/sonar-process/src/main/java/org/sonar/process/AesCipher.java137
-rw-r--r--server/process/sonar-process/src/main/java/org/sonar/process/Base64Cipher.java35
-rw-r--r--server/process/sonar-process/src/main/java/org/sonar/process/Cipher.java27
-rw-r--r--server/process/sonar-process/src/main/java/org/sonar/process/ConfigurationUtils.java75
-rw-r--r--server/process/sonar-process/src/main/java/org/sonar/process/Encryption.java64
-rw-r--r--server/process/sonar-process/src/main/java/org/sonar/process/JmxUtils.java54
-rw-r--r--server/process/sonar-process/src/main/java/org/sonar/process/MessageException.java36
-rw-r--r--server/process/sonar-process/src/main/java/org/sonar/process/MinimumViableSystem.java84
-rw-r--r--server/process/sonar-process/src/main/java/org/sonar/process/Monitor.java122
-rw-r--r--server/process/sonar-process/src/main/java/org/sonar/process/MonitoredProcess.java131
-rw-r--r--server/process/sonar-process/src/main/java/org/sonar/process/NetworkUtils.java37
-rw-r--r--server/process/sonar-process/src/main/java/org/sonar/process/ProcessLogging.java53
-rw-r--r--server/process/sonar-process/src/main/java/org/sonar/process/ProcessMXBean.java27
-rw-r--r--server/process/sonar-process/src/main/java/org/sonar/process/ProcessUtils.java70
-rw-r--r--server/process/sonar-process/src/main/java/org/sonar/process/ProcessWrapper.java345
-rw-r--r--server/process/sonar-process/src/main/java/org/sonar/process/Props.java106
-rw-r--r--server/process/sonar-process/src/main/java/org/sonar/process/Terminable.java30
-rw-r--r--server/process/sonar-process/src/test/java/org/sonar/process/AesCipherTest.java185
-rw-r--r--server/process/sonar-process/src/test/java/org/sonar/process/ConfigurationUtilsTest.java55
-rw-r--r--server/process/sonar-process/src/test/java/org/sonar/process/EncryptionTest.java59
-rw-r--r--server/process/sonar-process/src/test/java/org/sonar/process/MinimumViableSystemTest.java79
-rw-r--r--server/process/sonar-process/src/test/java/org/sonar/process/NetworkUtilsTest.java61
-rw-r--r--server/process/sonar-process/src/test/java/org/sonar/process/ProcessWrapperTest.java68
-rw-r--r--server/process/sonar-process/src/test/java/org/sonar/process/PropsTest.java96
-rw-r--r--server/process/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/aes_secret_key.txt1
-rw-r--r--server/process/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/bad_secret_key.txt1
-rw-r--r--server/process/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/non_trimmed_secret_key.txt3
-rw-r--r--server/process/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/other_secret_key.txt1
-rw-r--r--server/process/sonar-process/src/test/resources/org/sonar/process/LoggingTest/logback-access.xml1
-rw-r--r--server/process/sonar-process/src/test/resources/org/sonar/process/ProcessTest/sonar.properties212
-rw-r--r--server/process/sonar-process/src/test/resources/org/sonar/process/PropsTest/sonar.properties3
69 files changed, 2375 insertions, 13 deletions
diff --git a/server/pom.xml b/server/pom.xml
index 27d1fc77c4f..c0ec6370c42 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -11,8 +11,7 @@
<name>SonarQube :: Server :: Parent</name>
<modules>
- <module>sonar-process-test</module>
- <module>sonar-process</module>
+ <module>process</module>
<module>sonar-search</module>
<module>sonar-server</module>
<module>sonar-web</module>
diff --git a/server/process/pom.xml b/server/process/pom.xml
new file mode 100644
index 00000000000..b6cbcda18fa
--- /dev/null
+++ b/server/process/pom.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>server</artifactId>
+ <version>4.5-SNAPSHOT</version>
+ <relativePath>../</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>process</artifactId>
+ <packaging>pom</packaging>
+ <name>SonarQube :: Process :: Parent</name>
+
+ <modules>
+ <module>sonar-dummy-app</module>
+ <module>sonar-process</module>
+ <module>sonar-process-test</module>
+ </modules>
+</project>
diff --git a/server/sonar-process-test/pom.xml b/server/process/sonar-dummy-app/pom.xml
index c5b4876e700..4829cd17693 100644
--- a/server/sonar-process-test/pom.xml
+++ b/server/process/sonar-dummy-app/pom.xml
@@ -4,23 +4,22 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.codehaus.sonar</groupId>
- <artifactId>server</artifactId>
+ <artifactId>process</artifactId>
<version>4.5-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
- <artifactId>sonar-process-test</artifactId>
+ <artifactId>sonar-dummy-app</artifactId>
<packaging>jar</packaging>
- <name>SonarQube :: Process :: Test</name>
- <description>Wrapper to start Elasticsearch</description>
+ <name>SonarQube :: Process :: DummyApp</name>
+ <description>Dummy Application to test sonar-process</description>
<dependencies>
<dependency>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar-process</artifactId>
<version>${project.version}</version>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
@@ -86,7 +85,7 @@
<goal>shade</goal>
</goals>
<configuration>
- <keepDependenciesWithProvidedScope>true</keepDependenciesWithProvidedScope>
+ <keepDependenciesWithProvidedScope>false</keepDependenciesWithProvidedScope>
<minimizeJar>true</minimizeJar>
</configuration>
</execution>
diff --git a/server/sonar-process-test/src/main/java/org/sonar/application/DummyOkApp.java b/server/process/sonar-dummy-app/src/main/java/org/sonar/application/DummyOkApp.java
index 99adbd03125..99adbd03125 100644
--- a/server/sonar-process-test/src/main/java/org/sonar/application/DummyOkApp.java
+++ b/server/process/sonar-dummy-app/src/main/java/org/sonar/application/DummyOkApp.java
diff --git a/server/sonar-process-test/src/main/resources/org/sonar/application/logback.xml b/server/process/sonar-dummy-app/src/main/resources/org/sonar/application/logback.xml
index 933930557b9..933930557b9 100644
--- a/server/sonar-process-test/src/main/resources/org/sonar/application/logback.xml
+++ b/server/process/sonar-dummy-app/src/main/resources/org/sonar/application/logback.xml
diff --git a/server/sonar-process/pom.xml b/server/process/sonar-process-test/pom.xml
index 6c941681ba3..a02d03da5c2 100644
--- a/server/sonar-process/pom.xml
+++ b/server/process/sonar-process-test/pom.xml
@@ -4,15 +4,15 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.codehaus.sonar</groupId>
- <artifactId>server</artifactId>
+ <artifactId>process</artifactId>
<version>4.5-SNAPSHOT</version>
- <relativePath>..</relativePath>
+ <relativePath>../</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
- <artifactId>sonar-process</artifactId>
+ <artifactId>sonar-process-test</artifactId>
<packaging>jar</packaging>
- <name>SonarQube :: Process</name>
+ <name>SonarQube :: Process :: Tests</name>
<dependencies>
@@ -68,6 +68,18 @@
<artifactId>guava</artifactId>
<scope>test</scope>
</dependency>
+
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-dummy-app</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-process</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
<plugins>
@@ -86,7 +98,7 @@
<artifactItems>
<artifactItem>
<groupId>org.codehaus.sonar</groupId>
- <artifactId>sonar-process-test</artifactId>
+ <artifactId>sonar-dummy-app</artifactId>
<version>${project.version}</version>
<type>jar</type>
<outputDirectory>${project.build.directory}/dummyApp</outputDirectory>
diff --git a/server/sonar-process/src/main/java/org/sonar/process/AesCipher.java b/server/process/sonar-process-test/src/main/java/org/sonar/process/AesCipher.java
index 5b8102c044d..5b8102c044d 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/AesCipher.java
+++ b/server/process/sonar-process-test/src/main/java/org/sonar/process/AesCipher.java
diff --git a/server/sonar-process/src/main/java/org/sonar/process/Base64Cipher.java b/server/process/sonar-process-test/src/main/java/org/sonar/process/Base64Cipher.java
index 0ad817e7e9c..0ad817e7e9c 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/Base64Cipher.java
+++ b/server/process/sonar-process-test/src/main/java/org/sonar/process/Base64Cipher.java
diff --git a/server/sonar-process/src/main/java/org/sonar/process/Cipher.java b/server/process/sonar-process-test/src/main/java/org/sonar/process/Cipher.java
index 4d24cf2df56..4d24cf2df56 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/Cipher.java
+++ b/server/process/sonar-process-test/src/main/java/org/sonar/process/Cipher.java
diff --git a/server/sonar-process/src/main/java/org/sonar/process/ConfigurationUtils.java b/server/process/sonar-process-test/src/main/java/org/sonar/process/ConfigurationUtils.java
index 9d2cfd3fa74..9d2cfd3fa74 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/ConfigurationUtils.java
+++ b/server/process/sonar-process-test/src/main/java/org/sonar/process/ConfigurationUtils.java
diff --git a/server/sonar-process/src/main/java/org/sonar/process/Encryption.java b/server/process/sonar-process-test/src/main/java/org/sonar/process/Encryption.java
index cca05e6c780..cca05e6c780 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/Encryption.java
+++ b/server/process/sonar-process-test/src/main/java/org/sonar/process/Encryption.java
diff --git a/server/sonar-process/src/main/java/org/sonar/process/JmxUtils.java b/server/process/sonar-process-test/src/main/java/org/sonar/process/JmxUtils.java
index b88973e45c9..b88973e45c9 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/JmxUtils.java
+++ b/server/process/sonar-process-test/src/main/java/org/sonar/process/JmxUtils.java
diff --git a/server/sonar-process/src/main/java/org/sonar/process/MessageException.java b/server/process/sonar-process-test/src/main/java/org/sonar/process/MessageException.java
index 5b86ef66c64..5b86ef66c64 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/MessageException.java
+++ b/server/process/sonar-process-test/src/main/java/org/sonar/process/MessageException.java
diff --git a/server/sonar-process/src/main/java/org/sonar/process/MinimumViableSystem.java b/server/process/sonar-process-test/src/main/java/org/sonar/process/MinimumViableSystem.java
index 64526f56985..64526f56985 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/MinimumViableSystem.java
+++ b/server/process/sonar-process-test/src/main/java/org/sonar/process/MinimumViableSystem.java
diff --git a/server/sonar-process/src/main/java/org/sonar/process/Monitor.java b/server/process/sonar-process-test/src/main/java/org/sonar/process/Monitor.java
index d12e0f7e757..d12e0f7e757 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/Monitor.java
+++ b/server/process/sonar-process-test/src/main/java/org/sonar/process/Monitor.java
diff --git a/server/sonar-process/src/main/java/org/sonar/process/MonitoredProcess.java b/server/process/sonar-process-test/src/main/java/org/sonar/process/MonitoredProcess.java
index 670dc645555..670dc645555 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/MonitoredProcess.java
+++ b/server/process/sonar-process-test/src/main/java/org/sonar/process/MonitoredProcess.java
diff --git a/server/sonar-process/src/main/java/org/sonar/process/NetworkUtils.java b/server/process/sonar-process-test/src/main/java/org/sonar/process/NetworkUtils.java
index a037ce0113a..a037ce0113a 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/NetworkUtils.java
+++ b/server/process/sonar-process-test/src/main/java/org/sonar/process/NetworkUtils.java
diff --git a/server/sonar-process/src/main/java/org/sonar/process/ProcessLogging.java b/server/process/sonar-process-test/src/main/java/org/sonar/process/ProcessLogging.java
index 40b336bbc0b..40b336bbc0b 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/ProcessLogging.java
+++ b/server/process/sonar-process-test/src/main/java/org/sonar/process/ProcessLogging.java
diff --git a/server/sonar-process/src/main/java/org/sonar/process/ProcessMXBean.java b/server/process/sonar-process-test/src/main/java/org/sonar/process/ProcessMXBean.java
index d212c08ccdd..d212c08ccdd 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/ProcessMXBean.java
+++ b/server/process/sonar-process-test/src/main/java/org/sonar/process/ProcessMXBean.java
diff --git a/server/sonar-process/src/main/java/org/sonar/process/ProcessUtils.java b/server/process/sonar-process-test/src/main/java/org/sonar/process/ProcessUtils.java
index 92537412587..92537412587 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/ProcessUtils.java
+++ b/server/process/sonar-process-test/src/main/java/org/sonar/process/ProcessUtils.java
diff --git a/server/sonar-process/src/main/java/org/sonar/process/ProcessWrapper.java b/server/process/sonar-process-test/src/main/java/org/sonar/process/ProcessWrapper.java
index 8ff2f758974..8ff2f758974 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/ProcessWrapper.java
+++ b/server/process/sonar-process-test/src/main/java/org/sonar/process/ProcessWrapper.java
diff --git a/server/sonar-process/src/main/java/org/sonar/process/Props.java b/server/process/sonar-process-test/src/main/java/org/sonar/process/Props.java
index df673ff4c25..df673ff4c25 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/Props.java
+++ b/server/process/sonar-process-test/src/main/java/org/sonar/process/Props.java
diff --git a/server/sonar-process/src/main/java/org/sonar/process/Terminable.java b/server/process/sonar-process-test/src/main/java/org/sonar/process/Terminable.java
index 109e91c42da..109e91c42da 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/Terminable.java
+++ b/server/process/sonar-process-test/src/main/java/org/sonar/process/Terminable.java
diff --git a/server/sonar-process/src/test/java/org/sonar/process/AesCipherTest.java b/server/process/sonar-process-test/src/test/java/org/sonar/process/AesCipherTest.java
index 8350eafaa3e..8350eafaa3e 100644
--- a/server/sonar-process/src/test/java/org/sonar/process/AesCipherTest.java
+++ b/server/process/sonar-process-test/src/test/java/org/sonar/process/AesCipherTest.java
diff --git a/server/sonar-process/src/test/java/org/sonar/process/ConfigurationUtilsTest.java b/server/process/sonar-process-test/src/test/java/org/sonar/process/ConfigurationUtilsTest.java
index 6191eb2ba1f..6191eb2ba1f 100644
--- a/server/sonar-process/src/test/java/org/sonar/process/ConfigurationUtilsTest.java
+++ b/server/process/sonar-process-test/src/test/java/org/sonar/process/ConfigurationUtilsTest.java
diff --git a/server/sonar-process/src/test/java/org/sonar/process/EncryptionTest.java b/server/process/sonar-process-test/src/test/java/org/sonar/process/EncryptionTest.java
index 0c11856b0fa..0c11856b0fa 100644
--- a/server/sonar-process/src/test/java/org/sonar/process/EncryptionTest.java
+++ b/server/process/sonar-process-test/src/test/java/org/sonar/process/EncryptionTest.java
diff --git a/server/sonar-process/src/test/java/org/sonar/process/MinimumViableSystemTest.java b/server/process/sonar-process-test/src/test/java/org/sonar/process/MinimumViableSystemTest.java
index 11088902b14..11088902b14 100644
--- a/server/sonar-process/src/test/java/org/sonar/process/MinimumViableSystemTest.java
+++ b/server/process/sonar-process-test/src/test/java/org/sonar/process/MinimumViableSystemTest.java
diff --git a/server/sonar-process/src/test/java/org/sonar/process/NetworkUtilsTest.java b/server/process/sonar-process-test/src/test/java/org/sonar/process/NetworkUtilsTest.java
index 09f6a597209..09f6a597209 100644
--- a/server/sonar-process/src/test/java/org/sonar/process/NetworkUtilsTest.java
+++ b/server/process/sonar-process-test/src/test/java/org/sonar/process/NetworkUtilsTest.java
diff --git a/server/sonar-process/src/test/java/org/sonar/process/ProcessWrapperTest.java b/server/process/sonar-process-test/src/test/java/org/sonar/process/ProcessWrapperTest.java
index 460983290b0..460983290b0 100644
--- a/server/sonar-process/src/test/java/org/sonar/process/ProcessWrapperTest.java
+++ b/server/process/sonar-process-test/src/test/java/org/sonar/process/ProcessWrapperTest.java
diff --git a/server/sonar-process/src/test/java/org/sonar/process/PropsTest.java b/server/process/sonar-process-test/src/test/java/org/sonar/process/PropsTest.java
index 775d24a2a64..775d24a2a64 100644
--- a/server/sonar-process/src/test/java/org/sonar/process/PropsTest.java
+++ b/server/process/sonar-process-test/src/test/java/org/sonar/process/PropsTest.java
diff --git a/server/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/aes_secret_key.txt b/server/process/sonar-process-test/src/test/resources/org/sonar/process/AesCipherTest/aes_secret_key.txt
index 65b98c522da..65b98c522da 100644
--- a/server/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/aes_secret_key.txt
+++ b/server/process/sonar-process-test/src/test/resources/org/sonar/process/AesCipherTest/aes_secret_key.txt
diff --git a/server/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/bad_secret_key.txt b/server/process/sonar-process-test/src/test/resources/org/sonar/process/AesCipherTest/bad_secret_key.txt
index b33e179e5c8..b33e179e5c8 100644
--- a/server/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/bad_secret_key.txt
+++ b/server/process/sonar-process-test/src/test/resources/org/sonar/process/AesCipherTest/bad_secret_key.txt
diff --git a/server/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/non_trimmed_secret_key.txt b/server/process/sonar-process-test/src/test/resources/org/sonar/process/AesCipherTest/non_trimmed_secret_key.txt
index ab83e4adc03..ab83e4adc03 100644
--- a/server/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/non_trimmed_secret_key.txt
+++ b/server/process/sonar-process-test/src/test/resources/org/sonar/process/AesCipherTest/non_trimmed_secret_key.txt
diff --git a/server/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/other_secret_key.txt b/server/process/sonar-process-test/src/test/resources/org/sonar/process/AesCipherTest/other_secret_key.txt
index 23f5ecf5104..23f5ecf5104 100644
--- a/server/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/other_secret_key.txt
+++ b/server/process/sonar-process-test/src/test/resources/org/sonar/process/AesCipherTest/other_secret_key.txt
diff --git a/server/sonar-process/src/test/resources/org/sonar/process/LoggingTest/logback-access.xml b/server/process/sonar-process-test/src/test/resources/org/sonar/process/LoggingTest/logback-access.xml
index 298193e01fa..298193e01fa 100644
--- a/server/sonar-process/src/test/resources/org/sonar/process/LoggingTest/logback-access.xml
+++ b/server/process/sonar-process-test/src/test/resources/org/sonar/process/LoggingTest/logback-access.xml
diff --git a/server/sonar-process/src/test/resources/org/sonar/process/ProcessTest/sonar.properties b/server/process/sonar-process-test/src/test/resources/org/sonar/process/ProcessTest/sonar.properties
index 1577a214b3b..1577a214b3b 100644
--- a/server/sonar-process/src/test/resources/org/sonar/process/ProcessTest/sonar.properties
+++ b/server/process/sonar-process-test/src/test/resources/org/sonar/process/ProcessTest/sonar.properties
diff --git a/server/sonar-process/src/test/resources/org/sonar/process/PropsTest/sonar.properties b/server/process/sonar-process-test/src/test/resources/org/sonar/process/PropsTest/sonar.properties
index 5c06e58a32e..5c06e58a32e 100644
--- a/server/sonar-process/src/test/resources/org/sonar/process/PropsTest/sonar.properties
+++ b/server/process/sonar-process-test/src/test/resources/org/sonar/process/PropsTest/sonar.properties
diff --git a/server/process/sonar-process/pom.xml b/server/process/sonar-process/pom.xml
new file mode 100644
index 00000000000..42c05518fd5
--- /dev/null
+++ b/server/process/sonar-process/pom.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>process</artifactId>
+ <version>4.5-SNAPSHOT</version>
+ <relativePath>../</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>sonar-process</artifactId>
+ <packaging>jar</packaging>
+ <name>SonarQube :: Process</name>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-lang</groupId>
+ <artifactId>commons-lang</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.code.findbugs</groupId>
+ <artifactId>jsr305</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easytesting</groupId>
+ <artifactId>fest-assert</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/server/process/sonar-process/src/main/java/org/sonar/process/AesCipher.java b/server/process/sonar-process/src/main/java/org/sonar/process/AesCipher.java
new file mode 100644
index 00000000000..5b8102c044d
--- /dev/null
+++ b/server/process/sonar-process/src/main/java/org/sonar/process/AesCipher.java
@@ -0,0 +1,137 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.process;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.StringUtils;
+
+import javax.annotation.Nullable;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.File;
+import java.io.IOException;
+import java.security.Key;
+import java.security.SecureRandom;
+
+final class AesCipher extends Cipher {
+
+ // Can't be increased because of Java 6 policy files :
+ // https://confluence.terena.org/display/~visser/No+256+bit+ciphers+for+Java+apps
+ // http://java.sun.com/javase/6/webnotes/install/jre/README
+ public static final int KEY_SIZE_IN_BITS = 128;
+
+ private static final String CRYPTO_KEY = "AES";
+
+ /**
+ * Duplication from CoreProperties.ENCRYPTION_SECRET_KEY_PATH
+ */
+ static final String ENCRYPTION_SECRET_KEY_PATH = "sonar.secretKeyPath";
+
+ private String pathToSecretKey;
+
+ AesCipher(@Nullable String pathToSecretKey) {
+ this.pathToSecretKey = pathToSecretKey;
+ }
+
+ @Override
+ String encrypt(String clearText) {
+ try {
+ javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance(CRYPTO_KEY);
+ cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, loadSecretFile());
+ return new String(Base64.encodeBase64(cipher.doFinal(clearText.getBytes("UTF-8"))));
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ String decrypt(String encryptedText) {
+ try {
+ javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance(CRYPTO_KEY);
+ cipher.init(javax.crypto.Cipher.DECRYPT_MODE, loadSecretFile());
+ byte[] cipherData = cipher.doFinal(Base64.decodeBase64(StringUtils.trim(encryptedText)));
+ return new String(cipherData);
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * This method checks the existence of the file, but not the validity of the contained key.
+ */
+ boolean hasSecretKey() {
+ String path = getPathToSecretKey();
+ if (StringUtils.isNotBlank(path)) {
+ File file = new File(path);
+ return file.exists() && file.isFile();
+ }
+ return false;
+ }
+
+ private Key loadSecretFile() throws IOException {
+ String path = getPathToSecretKey();
+ return loadSecretFileFromFile(path);
+ }
+
+ Key loadSecretFileFromFile(@Nullable String path) throws IOException {
+ if (StringUtils.isBlank(path)) {
+ throw new IllegalStateException("Secret key not found. Please set the property " + ENCRYPTION_SECRET_KEY_PATH);
+ }
+ File file = new File(path);
+ if (!file.exists() || !file.isFile()) {
+ throw new IllegalStateException("The property " + ENCRYPTION_SECRET_KEY_PATH + " does not link to a valid file: " + path);
+ }
+ String s = FileUtils.readFileToString(file);
+ if (StringUtils.isBlank(s)) {
+ throw new IllegalStateException("No secret key in the file: " + path);
+ }
+ return new SecretKeySpec(Base64.decodeBase64(StringUtils.trim(s)), CRYPTO_KEY);
+ }
+
+ String generateRandomSecretKey() {
+ try {
+ KeyGenerator keyGen = KeyGenerator.getInstance(CRYPTO_KEY);
+ keyGen.init(KEY_SIZE_IN_BITS, new SecureRandom());
+ SecretKey secretKey = keyGen.generateKey();
+ return new String(Base64.encodeBase64(secretKey.getEncoded()));
+
+ } catch (Exception e) {
+ throw new IllegalStateException("Fail to generate secret key", e);
+ }
+ }
+
+ String getPathToSecretKey() {
+ if (StringUtils.isBlank(pathToSecretKey)) {
+ pathToSecretKey = new File(FileUtils.getUserDirectoryPath(), ".sonar/sonar-secret.txt").getPath();
+ }
+ return pathToSecretKey;
+ }
+
+ public void setPathToSecretKey(@Nullable String pathToSecretKey) {
+ this.pathToSecretKey = pathToSecretKey;
+ }
+}
diff --git a/server/process/sonar-process/src/main/java/org/sonar/process/Base64Cipher.java b/server/process/sonar-process/src/main/java/org/sonar/process/Base64Cipher.java
new file mode 100644
index 00000000000..0ad817e7e9c
--- /dev/null
+++ b/server/process/sonar-process/src/main/java/org/sonar/process/Base64Cipher.java
@@ -0,0 +1,35 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.process;
+
+import org.apache.commons.codec.binary.Base64;
+
+final class Base64Cipher extends Cipher {
+ @Override
+ String encrypt(String clearText) {
+ return new String(Base64.encodeBase64(clearText.getBytes()));
+ }
+
+ @Override
+ String decrypt(String encryptedText) {
+ return new String(Base64.decodeBase64(encryptedText));
+ }
+}
diff --git a/server/process/sonar-process/src/main/java/org/sonar/process/Cipher.java b/server/process/sonar-process/src/main/java/org/sonar/process/Cipher.java
new file mode 100644
index 00000000000..4d24cf2df56
--- /dev/null
+++ b/server/process/sonar-process/src/main/java/org/sonar/process/Cipher.java
@@ -0,0 +1,27 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.process;
+
+abstract class Cipher {
+ abstract String encrypt(String clearText);
+
+ abstract String decrypt(String encryptedText);
+}
diff --git a/server/process/sonar-process/src/main/java/org/sonar/process/ConfigurationUtils.java b/server/process/sonar-process/src/main/java/org/sonar/process/ConfigurationUtils.java
new file mode 100644
index 00000000000..9d2cfd3fa74
--- /dev/null
+++ b/server/process/sonar-process/src/main/java/org/sonar/process/ConfigurationUtils.java
@@ -0,0 +1,75 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.process;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.text.StrSubstitutor;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.Properties;
+
+public final class ConfigurationUtils {
+
+ private ConfigurationUtils() {
+ // Utility class
+ }
+
+ public static Properties interpolateVariables(Properties properties, Map<String, String> variables) {
+ Properties result = new Properties();
+ Enumeration keys = properties.keys();
+ while (keys.hasMoreElements()) {
+ String key = (String) keys.nextElement();
+ String value = (String) properties.get(key);
+ String interpolatedValue = StrSubstitutor.replace(value, variables, "${env:", "}");
+ result.setProperty(key, interpolatedValue);
+ }
+ return result;
+ }
+
+ public static Props loadPropsFromCommandLineArgs(String[] args) {
+ if (args.length != 1) {
+ throw new IllegalStateException("Only a single command-line argument is accepted " +
+ "(absolute path to configuration file)");
+ }
+
+ File propertyFile = new File(args[0]);
+ if (!propertyFile.exists()) {
+ throw new IllegalStateException("Property file '" + args[0] + "' does not exist! ");
+ }
+
+ Properties properties = new Properties();
+ FileReader reader = null;
+ try {
+ reader = new FileReader(propertyFile);
+ properties.load(reader);
+ } catch (IOException e) {
+ throw new IllegalStateException("Could not read properties from file '" + args[0] + "'", e);
+ } finally {
+ IOUtils.closeQuietly(reader);
+ FileUtils.deleteQuietly(propertyFile);
+ }
+ return new Props(properties);
+ }
+}
diff --git a/server/process/sonar-process/src/main/java/org/sonar/process/Encryption.java b/server/process/sonar-process/src/main/java/org/sonar/process/Encryption.java
new file mode 100644
index 00000000000..cca05e6c780
--- /dev/null
+++ b/server/process/sonar-process/src/main/java/org/sonar/process/Encryption.java
@@ -0,0 +1,64 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.process;
+
+import javax.annotation.Nullable;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @since 3.0
+ */
+public final class Encryption {
+
+ private static final String BASE64_ALGORITHM = "b64";
+
+ private static final String AES_ALGORITHM = "aes";
+ private final AesCipher aesCipher;
+
+ private final Map<String, Cipher> ciphers = new HashMap<String, Cipher>();
+ private static final Pattern ENCRYPTED_PATTERN = Pattern.compile("\\{(.*?)\\}(.*)");
+
+ public Encryption(@Nullable String pathToSecretKey) {
+ aesCipher = new AesCipher(pathToSecretKey);
+ ciphers.put(BASE64_ALGORITHM, new Base64Cipher());
+ ciphers.put(AES_ALGORITHM, aesCipher);
+ }
+
+ public boolean isEncrypted(String value) {
+ return value.indexOf('{') == 0 && value.indexOf('}') > 1;
+ }
+
+ public String decrypt(String encryptedText) {
+ Matcher matcher = ENCRYPTED_PATTERN.matcher(encryptedText);
+ if (matcher.matches()) {
+ Cipher cipher = ciphers.get(matcher.group(1).toLowerCase(Locale.ENGLISH));
+ if (cipher != null) {
+ return cipher.decrypt(matcher.group(2));
+ }
+ }
+ return encryptedText;
+ }
+
+}
diff --git a/server/process/sonar-process/src/main/java/org/sonar/process/JmxUtils.java b/server/process/sonar-process/src/main/java/org/sonar/process/JmxUtils.java
new file mode 100644
index 00000000000..b88973e45c9
--- /dev/null
+++ b/server/process/sonar-process/src/main/java/org/sonar/process/JmxUtils.java
@@ -0,0 +1,54 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.process;
+
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import java.lang.management.ManagementFactory;
+
+public class JmxUtils {
+ private JmxUtils() {
+ // only static stuff
+ }
+
+ public static final String WEB_SERVER_NAME = "web";
+ public static final String SEARCH_SERVER_NAME = "search";
+
+ public static ObjectName objectName(String name) {
+ try {
+ return new ObjectName("org.sonar", "name", name);
+ } catch (MalformedObjectNameException e) {
+ throw new IllegalStateException("Cannot create ObjectName for " + name, e);
+ }
+ }
+
+ public static void registerMBean(Object mbean, String name) {
+ try {
+ MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
+ mbeanServer.registerMBean(mbean, objectName(name));
+ } catch (RuntimeException re) {
+ throw re;
+ } catch (Exception e) {
+ throw new IllegalStateException("Fail to register JMX MBean named " + name, e);
+ }
+ }
+}
diff --git a/server/process/sonar-process/src/main/java/org/sonar/process/MessageException.java b/server/process/sonar-process/src/main/java/org/sonar/process/MessageException.java
new file mode 100644
index 00000000000..5b86ef66c64
--- /dev/null
+++ b/server/process/sonar-process/src/main/java/org/sonar/process/MessageException.java
@@ -0,0 +1,36 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.process;
+
+public class MessageException extends RuntimeException {
+ public MessageException(String message) {
+ super(message);
+ }
+
+ /**
+ * Does not fill in the stack trace
+ *
+ * @see Throwable#fillInStackTrace()
+ */
+ @Override
+ public synchronized Throwable fillInStackTrace() {
+ return this;
+ }
+}
diff --git a/server/process/sonar-process/src/main/java/org/sonar/process/MinimumViableSystem.java b/server/process/sonar-process/src/main/java/org/sonar/process/MinimumViableSystem.java
new file mode 100644
index 00000000000..64526f56985
--- /dev/null
+++ b/server/process/sonar-process/src/main/java/org/sonar/process/MinimumViableSystem.java
@@ -0,0 +1,84 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.process;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.StringUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class MinimumViableSystem {
+
+ private final Map<String, String> requiredJavaOptions = new HashMap<String, String>();
+
+ public MinimumViableSystem setRequiredJavaOption(String propertyKey, String expectedValue) {
+ requiredJavaOptions.put(propertyKey, expectedValue);
+ return this;
+ }
+
+ /**
+ * Entry point for all checks
+ */
+ public void check() {
+ checkJavaVersion();
+ checkJavaOptions();
+ checkWritableTempDir();
+ }
+
+ /**
+ * Verify that temp directory is writable
+ */
+ private void checkWritableTempDir() {
+ String tempPath = System.getProperty("java.io.tmpdir");
+ try {
+ File tempFile = File.createTempFile("check", "tmp", new File(tempPath));
+ FileUtils.deleteQuietly(tempFile);
+ } catch (IOException e) {
+ throw new MessageException(String.format(
+ "Temp directory is not writable: %s. Reason: %s", tempPath, e.getMessage()));
+ }
+ }
+
+ void checkJavaOptions() {
+ for (Map.Entry<String, String> entry : requiredJavaOptions.entrySet()) {
+ String value = System.getProperty(entry.getKey());
+ if (!StringUtils.equals(value, entry.getValue())) {
+ throw new MessageException(String.format(
+ "JVM option '%s' must be set to '%s'. Got '%s'", entry.getKey(), entry.getValue(), StringUtils.defaultString(value)));
+ }
+ }
+ }
+
+ void checkJavaVersion() {
+ String javaVersion = System.getProperty("java.version");
+ checkJavaVersion(javaVersion);
+ }
+
+ void checkJavaVersion(String javaVersion) {
+ if (javaVersion.startsWith("1.3") || javaVersion.startsWith("1.4") || javaVersion.startsWith("1.5")) {
+ // still better than "java.lang.UnsupportedClassVersionError: Unsupported major.minor version 49.0
+ throw new MessageException(String.format("Minimal required Java version is 1.6. Got %s.", javaVersion));
+ }
+ }
+
+}
diff --git a/server/process/sonar-process/src/main/java/org/sonar/process/Monitor.java b/server/process/sonar-process/src/main/java/org/sonar/process/Monitor.java
new file mode 100644
index 00000000000..d12e0f7e757
--- /dev/null
+++ b/server/process/sonar-process/src/main/java/org/sonar/process/Monitor.java
@@ -0,0 +1,122 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.process;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+public class Monitor extends Thread implements Terminable {
+
+ private static final long PING_DELAY_MS = 1000L;
+ private final static Logger LOGGER = LoggerFactory.getLogger(Monitor.class);
+
+ private volatile List<ProcessWrapper> processes;
+ private final ScheduledFuture<?> watch;
+ private final ScheduledExecutorService monitor;
+
+ /**
+ * Starts another thread to send ping to all registered processes
+ */
+ public Monitor() {
+ super("Process Monitor");
+ processes = new ArrayList<ProcessWrapper>();
+ monitor = Executors.newScheduledThreadPool(1);
+ watch = monitor.scheduleWithFixedDelay(new ProcessWatch(), 0L, PING_DELAY_MS, TimeUnit.MILLISECONDS);
+ }
+
+ private class ProcessWatch extends Thread {
+ private ProcessWatch() {
+ super("Process Ping");
+ }
+
+ @Override
+ public void run() {
+ for (ProcessWrapper process : processes) {
+ try {
+ ProcessMXBean mBean = process.getProcessMXBean();
+ if (mBean != null) {
+ mBean.ping();
+ }
+ } catch (Exception e) {
+ // fail to ping, do nothing
+ }
+ }
+ }
+ }
+
+ /**
+ * Registers and monitors process. Note that process is probably not ready yet.
+ */
+ public void registerProcess(ProcessWrapper process) throws InterruptedException {
+ processes.add(process);
+ // starts a monitoring thread
+ process.start();
+ }
+
+ /**
+ * Check continuously that registered processes are still up. If any process is down or does not answer to pings
+ * during the max allowed period, then thread exits.
+ */
+ @Override
+ public void run() {
+ try {
+ boolean ok = true;
+ while (ok) {
+ for (ProcessWrapper process : processes) {
+ if (!ProcessUtils.isAlive(process.process())) {
+ LOGGER.info("{} is down, stopping all other processes", process.getName());
+ ok = false;
+ interrupt();
+ }
+ }
+ if (ok) {
+ Thread.sleep(PING_DELAY_MS);
+ }
+ }
+ } catch (InterruptedException e) {
+ LOGGER.debug("Monitoring thread is interrupted");
+ } finally {
+ terminate();
+ }
+ }
+
+ @Override
+ public void terminate() {
+ if (!monitor.isShutdown()) {
+ monitor.shutdownNow();
+ }
+ if (!watch.isCancelled()) {
+ watch.cancel(true);
+ }
+
+ for (int i = processes.size() - 1; i >= 0; i--) {
+ processes.get(i).terminate();
+ }
+ processes.clear();
+ interrupt();
+ }
+}
diff --git a/server/process/sonar-process/src/main/java/org/sonar/process/MonitoredProcess.java b/server/process/sonar-process/src/main/java/org/sonar/process/MonitoredProcess.java
new file mode 100644
index 00000000000..670dc645555
--- /dev/null
+++ b/server/process/sonar-process/src/main/java/org/sonar/process/MonitoredProcess.java
@@ -0,0 +1,131 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.process;
+
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+public abstract class MonitoredProcess implements ProcessMXBean {
+
+ public static final String NAME_PROPERTY = "pName";
+ private static final long AUTOKILL_TIMEOUT_MS = 15000L;
+ private static final long AUTOKILL_CHECK_DELAY_MS = 5000L;
+ public static final String MISSING_NAME_ARGUMENT = "Missing Name argument";
+
+ private Long lastPing;
+ private final String name;
+ protected final Props props;
+ private ScheduledFuture<?> pingTask = null;
+ private ScheduledExecutorService monitor;
+
+ protected MonitoredProcess(Props props) throws Exception {
+ this.props = props;
+ this.name = props.of(NAME_PROPERTY);
+
+ // Testing required properties
+ if (StringUtils.isEmpty(name)) {
+ throw new IllegalStateException(MISSING_NAME_ARGUMENT);
+ }
+
+ JmxUtils.registerMBean(this, name);
+ ProcessUtils.addSelfShutdownHook(this);
+ }
+
+ public final void start() {
+ if (monitor != null) {
+ throw new IllegalStateException("Already started");
+ }
+
+ Logger logger = LoggerFactory.getLogger(getClass());
+ logger.debug("Process[{}] starting", name);
+ scheduleAutokill();
+ doStart();
+ logger.debug("Process[{}] started", name);
+ }
+
+ /**
+ * If the process does not receive pings during the max allowed period, then
+ * process auto-kills
+ */
+ private void scheduleAutokill() {
+ final Runnable breakOnMissingPing = new Runnable() {
+ @Override
+ public void run() {
+ long time = System.currentTimeMillis();
+ if (time - lastPing > AUTOKILL_TIMEOUT_MS) {
+ LoggerFactory.getLogger(getClass()).info(String.format(
+ "Did not receive any ping during %d seconds. Shutting down.", AUTOKILL_TIMEOUT_MS / 1000));
+ terminate();
+ }
+ }
+ };
+ lastPing = System.currentTimeMillis();
+ monitor = Executors.newScheduledThreadPool(1);
+ pingTask = monitor.scheduleAtFixedRate(breakOnMissingPing, AUTOKILL_CHECK_DELAY_MS, AUTOKILL_CHECK_DELAY_MS, TimeUnit.MILLISECONDS);
+ }
+
+ @Override
+ public final long ping() {
+ this.lastPing = System.currentTimeMillis();
+ return lastPing;
+ }
+
+ @Override
+ public final void terminate() {
+ if (monitor != null) {
+ Logger logger = LoggerFactory.getLogger(getClass());
+ logger.debug("Process[{}] terminating", name);
+ monitor.shutdownNow();
+ monitor = null;
+ if (pingTask != null) {
+ pingTask.cancel(true);
+ pingTask = null;
+ }
+ try {
+ doTerminate();
+ } catch (Exception e) {
+ LoggerFactory.getLogger(getClass()).error("Fail to terminate " + name, e);
+ // do not propagate exception
+ }
+ logger.debug("Process[{}] terminated", name);
+ }
+ }
+
+ @Override
+ public final boolean isReady() {
+ try {
+ return doIsReady();
+ } catch (Exception ignored) {
+ return false;
+ }
+ }
+
+ protected abstract void doStart();
+
+ protected abstract void doTerminate();
+
+ protected abstract boolean doIsReady();
+}
diff --git a/server/process/sonar-process/src/main/java/org/sonar/process/NetworkUtils.java b/server/process/sonar-process/src/main/java/org/sonar/process/NetworkUtils.java
new file mode 100644
index 00000000000..a037ce0113a
--- /dev/null
+++ b/server/process/sonar-process/src/main/java/org/sonar/process/NetworkUtils.java
@@ -0,0 +1,37 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.process;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+
+public class NetworkUtils {
+
+ public static int freePort() {
+ try {
+ ServerSocket s = new ServerSocket(0);
+ int port = s.getLocalPort();
+ s.close();
+ return port;
+ } catch (IOException e) {
+ throw new IllegalStateException("Can not find an open network port", e);
+ }
+ }
+}
diff --git a/server/process/sonar-process/src/main/java/org/sonar/process/ProcessLogging.java b/server/process/sonar-process/src/main/java/org/sonar/process/ProcessLogging.java
new file mode 100644
index 00000000000..40b336bbc0b
--- /dev/null
+++ b/server/process/sonar-process/src/main/java/org/sonar/process/ProcessLogging.java
@@ -0,0 +1,53 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.process;
+
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.joran.JoranConfigurator;
+import ch.qos.logback.core.joran.spi.JoranException;
+import ch.qos.logback.core.util.StatusPrinter;
+import org.slf4j.LoggerFactory;
+
+public class ProcessLogging {
+
+ private static final String PATH_LOGS_PROPERTY = "sonar.path.logs";
+
+ public void configure(Props props, String logbackXmlResource) {
+ LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
+ try {
+ JoranConfigurator configurator = new JoranConfigurator();
+ configurator.setContext(context);
+ context.reset();
+ context.putProperty(PATH_LOGS_PROPERTY, props.of(PATH_LOGS_PROPERTY));
+ doConfigure(configurator, logbackXmlResource);
+ } catch (JoranException je) {
+ // StatusPrinter will handle this
+ }
+ StatusPrinter.printInCaseOfErrorsOrWarnings(context);
+
+ }
+
+ /**
+ * Extracted only for unit testing
+ */
+ void doConfigure(JoranConfigurator configurator, String logbackXmlResource) throws JoranException {
+ configurator.doConfigure(getClass().getResource(logbackXmlResource));
+ }
+}
diff --git a/server/process/sonar-process/src/main/java/org/sonar/process/ProcessMXBean.java b/server/process/sonar-process/src/main/java/org/sonar/process/ProcessMXBean.java
new file mode 100644
index 00000000000..d212c08ccdd
--- /dev/null
+++ b/server/process/sonar-process/src/main/java/org/sonar/process/ProcessMXBean.java
@@ -0,0 +1,27 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.process;
+
+public interface ProcessMXBean extends Terminable {
+
+ boolean isReady();
+
+ long ping();
+}
diff --git a/server/process/sonar-process/src/main/java/org/sonar/process/ProcessUtils.java b/server/process/sonar-process/src/main/java/org/sonar/process/ProcessUtils.java
new file mode 100644
index 00000000000..92537412587
--- /dev/null
+++ b/server/process/sonar-process/src/main/java/org/sonar/process/ProcessUtils.java
@@ -0,0 +1,70 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.process;
+
+import org.apache.commons.io.IOUtils;
+
+import javax.annotation.Nullable;
+
+public class ProcessUtils {
+ private ProcessUtils() {
+ // only static stuff
+ }
+
+ public static boolean isAlive(@Nullable Process process) {
+ if (process == null) {
+ return false;
+ }
+ try {
+ process.exitValue();
+ return false;
+ } catch (IllegalThreadStateException e) {
+ return true;
+ }
+ }
+
+ public static void destroyQuietly(@Nullable Process process) {
+ if (process != null && isAlive(process)) {
+ try {
+ process.destroy();
+ } catch (Exception ignored) {
+ // ignored
+ }
+ }
+ }
+
+ public static void addSelfShutdownHook(final Terminable terminable) {
+ Thread shutdownHook = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ terminable.terminate();
+ }
+ });
+ Runtime.getRuntime().addShutdownHook(shutdownHook);
+ }
+
+ public static void closeStreams(@Nullable Process process) {
+ if (process != null) {
+ IOUtils.closeQuietly(process.getInputStream());
+ IOUtils.closeQuietly(process.getOutputStream());
+ IOUtils.closeQuietly(process.getErrorStream());
+ }
+ }
+}
diff --git a/server/process/sonar-process/src/main/java/org/sonar/process/ProcessWrapper.java b/server/process/sonar-process/src/main/java/org/sonar/process/ProcessWrapper.java
new file mode 100644
index 00000000000..8ff2f758974
--- /dev/null
+++ b/server/process/sonar-process/src/main/java/org/sonar/process/ProcessWrapper.java
@@ -0,0 +1,345 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.process;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import javax.management.JMX;
+import javax.management.MBeanServerConnection;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXServiceURL;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Fork and monitor a new process
+ */
+public class ProcessWrapper extends Thread implements Terminable {
+
+ private final static Logger LOGGER = LoggerFactory.getLogger(ProcessWrapper.class);
+
+ public static final long READY_TIMEOUT_MS = 300000L;
+
+ private String processName, className;
+ private int jmxPort = -1;
+ private final List<String> javaOpts = new ArrayList<String>();
+ private final List<String> classpath = new ArrayList<String>();
+ private final Map<String, String> envProperties = new HashMap<String, String>();
+ private final Properties properties = new Properties();
+ private File workDir;
+ private Process process;
+ private StreamGobbler errorGobbler;
+ private StreamGobbler outputGobbler;
+ private ProcessMXBean processMXBean;
+
+ public ProcessWrapper(String processName) {
+ super(processName);
+ this.processName = processName;
+ }
+
+ public ProcessWrapper setClassName(String s) {
+ this.className = s;
+ return this;
+ }
+
+ public ProcessWrapper setEnvProperty(String key, String value) {
+ envProperties.put(key, value);
+ return this;
+ }
+
+ public ProcessWrapper addProperties(Properties p) {
+ properties.putAll(p);
+ return this;
+ }
+
+ public ProcessWrapper addProperty(String key, String value) {
+ properties.setProperty(key, value);
+ return this;
+ }
+
+ public ProcessWrapper addJavaOpts(String s) {
+ Collections.addAll(javaOpts, s.split(" "));
+ return this;
+ }
+
+ public ProcessWrapper addClasspath(String s) {
+ classpath.add(s);
+ return this;
+ }
+
+ public ProcessWrapper setJmxPort(int i) {
+ this.jmxPort = i;
+ return this;
+ }
+
+ public ProcessWrapper setWorkDir(File d) {
+ this.workDir = d;
+ return this;
+ }
+
+ @CheckForNull
+ Process process() {
+ return process;
+ }
+
+ /**
+ * Execute command-line and connects to JMX RMI.
+ * @return true on success, false if bad command-line or process failed to start JMX RMI
+ */
+ public boolean execute() {
+ List<String> command = new ArrayList<String>();
+ try {
+ command.add(buildJavaCommand());
+ command.addAll(javaOpts);
+ command.addAll(buildJMXOptions());
+ command.addAll(buildClasspath());
+ command.add(className);
+ command.add(buildPropertiesFile().getAbsolutePath());
+
+ ProcessBuilder processBuilder = new ProcessBuilder();
+ processBuilder.command(command);
+ processBuilder.directory(workDir);
+ processBuilder.environment().putAll(envProperties);
+ LOGGER.info("starting {}: {}", getName(), StringUtils.join(command, " "));
+ process = processBuilder.start();
+ errorGobbler = new StreamGobbler(process.getErrorStream(), this.getName() + "-ERROR");
+ outputGobbler = new StreamGobbler(process.getInputStream(), this.getName());
+ outputGobbler.start();
+ errorGobbler.start();
+ processMXBean = waitForJMX();
+ if (processMXBean == null) {
+ terminate();
+ return false;
+ }
+ return true;
+ } catch (Exception e) {
+ throw new IllegalStateException("Fail to start command: " + StringUtils.join(command, " "), e);
+ }
+ }
+
+ @Override
+ public void run() {
+ try {
+ if (ProcessUtils.isAlive(process)) {
+ process.waitFor();
+ }
+ } catch (Exception e) {
+ LOGGER.info("ProcessThread has been interrupted. Killing process.");
+ } finally {
+ waitUntilFinish(outputGobbler);
+ waitUntilFinish(errorGobbler);
+ ProcessUtils.closeStreams(process);
+ this.interrupt();
+ }
+ }
+
+ public boolean isReady() {
+ return processMXBean != null && processMXBean.isReady();
+ }
+
+ public ProcessMXBean getProcessMXBean() {
+ return processMXBean;
+ }
+
+ private void waitUntilFinish(@Nullable Thread thread) {
+ if (thread != null) {
+ try {
+ thread.join();
+ } catch (InterruptedException e) {
+ LOGGER.error("InterruptedException while waiting finish of " + thread.getName() + " in process '" + getName() + "'", e);
+ }
+ }
+ }
+
+ private String buildJavaCommand() {
+ String separator = System.getProperty("file.separator");
+ return System.getProperty("java.home")
+ + separator + "bin" + separator + "java";
+ }
+
+ private List<String> buildJMXOptions() throws Exception {
+ if (jmxPort < 1) {
+ throw new IllegalStateException("JMX port is not set");
+ }
+ return Arrays.asList(
+ "-Dcom.sun.management.jmxremote",
+ "-Dcom.sun.management.jmxremote.port=" + jmxPort,
+ "-Dcom.sun.management.jmxremote.authenticate=false",
+ "-Dcom.sun.management.jmxremote.ssl=false",
+ "-Djava.rmi.server.hostname=" + localAddress());
+ }
+
+ private List<String> buildClasspath() {
+ return Arrays.asList("-cp", StringUtils.join(classpath, System.getProperty("path.separator")));
+ }
+
+ private File buildPropertiesFile() {
+ File propertiesFile = null;
+ try {
+ propertiesFile = File.createTempFile("sq-conf", "properties");
+ Properties props = new Properties();
+ props.putAll(properties);
+ props.put(MonitoredProcess.NAME_PROPERTY, processName);
+ OutputStream out = new FileOutputStream(propertiesFile);
+ props.store(out, "Temporary properties file for Process [" + getName() + "]");
+ out.close();
+ return propertiesFile;
+ } catch (IOException e) {
+ throw new IllegalStateException("Cannot write temporary settings to " + propertiesFile, e);
+ }
+ }
+
+ /**
+ * Wait for JMX RMI to be ready. Return <code>null</code>
+ */
+ @CheckForNull
+ private ProcessMXBean waitForJMX() throws Exception {
+ String loopbackAddress = localAddress();
+ String path = "/jndi/rmi://" + loopbackAddress + ":" + jmxPort + "/jmxrmi";
+ JMXServiceURL jmxUrl = new JMXServiceURL("rmi", loopbackAddress, jmxPort, path);
+
+ for (int i = 0; i < 5; i++) {
+ try {
+ Thread.sleep(1000L);
+ LOGGER.debug("Try #{} to connect to JMX server for process '{}'", i, processName);
+ JMXConnector jmxConnector = JMXConnectorFactory.connect(jmxUrl, null);
+ MBeanServerConnection mBeanServer = jmxConnector.getMBeanServerConnection();
+ ProcessMXBean bean = JMX.newMBeanProxy(mBeanServer, JmxUtils.objectName(processName), ProcessMXBean.class);
+ return bean;
+ } catch (Exception ignored) {
+ // ignored
+ }
+ }
+ // failed to connect
+ return null;
+ }
+
+ private String localAddress() {
+ // TODO to be replaced by InetAddress.getLoopbackAddress() in Java 7
+ return "127.0.0.1";
+ }
+
+ @Override
+ public void terminate() {
+ if (processMXBean != null && process != null) {
+ LOGGER.info("{} stopping", getName());
+ // Send the terminate command to process in order to gracefully shutdown.
+ // Then hardly kill it if it didn't terminate in 30 seconds
+ ScheduledExecutorService killer = Executors.newScheduledThreadPool(1);
+ try {
+ Runnable killerTask = new Runnable() {
+ @Override
+ public void run() {
+ ProcessUtils.destroyQuietly(process);
+ }
+ };
+
+ ScheduledFuture killerFuture = killer.schedule(killerTask, 30, TimeUnit.SECONDS);
+ processMXBean.terminate();
+ killerFuture.cancel(true);
+ LOGGER.info("{} stopped", getName());
+
+ } catch (Exception ignored) {
+ // ignore
+
+ } finally {
+ killer.shutdownNow();
+ }
+ } else {
+ // process is not monitored through JMX, but killing it though
+ ProcessUtils.destroyQuietly(process);
+ }
+ processMXBean = null;
+ }
+
+ public boolean waitForReady() throws InterruptedException {
+ if (processMXBean == null) {
+ return false;
+ }
+ long now = 0;
+ long wait = 500L;
+ while (now < READY_TIMEOUT_MS) {
+ try {
+ if (processMXBean == null) {
+ return false;
+ }
+ if (processMXBean.isReady()) {
+ return true;
+ }
+ } catch (Exception e) {
+ // ignore
+ }
+ Thread.sleep(wait);
+ now += wait;
+ }
+ return false;
+ }
+
+ private static class StreamGobbler extends Thread {
+ private final InputStream is;
+ private final Logger logger;
+
+ StreamGobbler(InputStream is, String name) {
+ super(name + "_ProcessStreamGobbler");
+ this.is = is;
+ this.logger = LoggerFactory.getLogger(name);
+ }
+
+ @Override
+ public void run() {
+ InputStreamReader isr = new InputStreamReader(is);
+ BufferedReader br = new BufferedReader(isr);
+ try {
+ String line;
+ while ((line = br.readLine()) != null) {
+ logger.info(line);
+ }
+ } catch (Exception ignored) {
+ // ignored
+
+ } finally {
+ IOUtils.closeQuietly(br);
+ IOUtils.closeQuietly(isr);
+ }
+ }
+ }
+}
diff --git a/server/process/sonar-process/src/main/java/org/sonar/process/Props.java b/server/process/sonar-process/src/main/java/org/sonar/process/Props.java
new file mode 100644
index 00000000000..df673ff4c25
--- /dev/null
+++ b/server/process/sonar-process/src/main/java/org/sonar/process/Props.java
@@ -0,0 +1,106 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.process;
+
+import org.apache.commons.lang.StringUtils;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+import java.io.File;
+import java.util.Properties;
+
+public class Props {
+
+ private final Properties props;
+ private final Encryption encryption;
+
+ public Props(Properties props) {
+ this.props = props;
+ this.encryption = new Encryption(props.getProperty(AesCipher.ENCRYPTION_SECRET_KEY_PATH));
+ }
+
+ public boolean contains(String key) {
+ return props.containsKey(key);
+ }
+
+ @CheckForNull
+ public String of(String key) {
+ String value = props.getProperty(key);
+ if (value != null && encryption.isEncrypted(value)) {
+ value = encryption.decrypt(value);
+ }
+ return value;
+ }
+
+ public String of(String key, @Nullable String defaultValue) {
+ String s = of(key);
+ return s == null ? defaultValue : s;
+ }
+
+ public boolean booleanOf(String key) {
+ String s = of(key);
+ return s != null && Boolean.parseBoolean(s);
+ }
+
+ public boolean booleanOf(String key, boolean defaultValue) {
+ String s = of(key);
+ return s != null ? Boolean.parseBoolean(s) : defaultValue;
+ }
+
+ @CheckForNull
+ public File fileOf(String key) {
+ String s = of(key);
+ return s != null ? new File(s) : null;
+ }
+
+ public Integer intOf(String key) {
+ String s = of(key);
+ if (s != null && !"".equals(s)) {
+ try {
+ return Integer.parseInt(s);
+ } catch (NumberFormatException e) {
+ throw new IllegalStateException("Value of property " + key + " is not an integer: " + s, e);
+ }
+ }
+ return null;
+ }
+
+ public int intOf(String key, int defaultValue) {
+ Integer i = intOf(key);
+ return i == null ? defaultValue : i;
+ }
+
+ public Properties rawProperties() {
+ return props;
+ }
+
+ public Props set(String key, @Nullable String value) {
+ props.setProperty(key, value);
+ return this;
+ }
+
+ public void setDefault(String key, String value) {
+ String s = props.getProperty(key);
+ if (StringUtils.isBlank(s)) {
+ props.setProperty(key, value);
+ }
+ }
+}
diff --git a/server/process/sonar-process/src/main/java/org/sonar/process/Terminable.java b/server/process/sonar-process/src/main/java/org/sonar/process/Terminable.java
new file mode 100644
index 00000000000..109e91c42da
--- /dev/null
+++ b/server/process/sonar-process/src/main/java/org/sonar/process/Terminable.java
@@ -0,0 +1,30 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.process;
+
+/**
+ * This term "terminate" is used in order to not conflict with {@link Thread#stop()}.
+ */
+public interface Terminable {
+ /**
+ * Stops pending work. Must <b>not</b> throw an exception on error.
+ */
+ void terminate();
+}
diff --git a/server/process/sonar-process/src/test/java/org/sonar/process/AesCipherTest.java b/server/process/sonar-process/src/test/java/org/sonar/process/AesCipherTest.java
new file mode 100644
index 00000000000..8350eafaa3e
--- /dev/null
+++ b/server/process/sonar-process/src/test/java/org/sonar/process/AesCipherTest.java
@@ -0,0 +1,185 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.process;
+
+import com.google.common.io.Resources;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang.StringUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import javax.crypto.BadPaddingException;
+import java.io.File;
+import java.security.InvalidKeyException;
+import java.security.Key;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.fest.assertions.Fail.fail;
+
+
+public class AesCipherTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void generateRandomSecretKey() {
+ AesCipher cipher = new AesCipher(null);
+
+ String key = cipher.generateRandomSecretKey();
+
+ assertThat(StringUtils.isNotBlank(key)).isTrue();
+ assertThat(Base64.isArrayByteBase64(key.getBytes())).isTrue();
+ }
+
+ @Test
+ public void encrypt() throws Exception {
+ AesCipher cipher = new AesCipher(pathToSecretKey());
+
+ String encryptedText = cipher.encrypt("this is a secret");
+
+ assertThat(StringUtils.isNotBlank(encryptedText)).isTrue();
+ assertThat(Base64.isArrayByteBase64(encryptedText.getBytes())).isTrue();
+ }
+
+ @Test
+ public void encrypt_bad_key() throws Exception {
+ thrown.expect(RuntimeException.class);
+ thrown.expectMessage("Invalid AES key");
+
+ AesCipher cipher = new AesCipher(getPath("bad_secret_key.txt"));
+
+ cipher.encrypt("this is a secret");
+ }
+
+ @Test
+ public void decrypt() throws Exception {
+ AesCipher cipher = new AesCipher(pathToSecretKey());
+
+ // the following value has been encrypted with the key /org/sonar/api/config/AesCipherTest/aes_secret_key.txt
+ String clearText = cipher.decrypt("9mx5Zq4JVyjeChTcVjEide4kWCwusFl7P2dSVXtg9IY=");
+
+ assertThat(clearText).isEqualTo("this is a secret");
+ }
+
+ @Test
+ public void decrypt_bad_key() throws Exception {
+ AesCipher cipher = new AesCipher(getPath("bad_secret_key.txt"));
+
+ try {
+ cipher.decrypt("9mx5Zq4JVyjeChTcVjEide4kWCwusFl7P2dSVXtg9IY=");
+ fail();
+
+ } catch (RuntimeException e) {
+ assertThat(e.getCause()).isInstanceOf(InvalidKeyException.class);
+ }
+ }
+
+ @Test
+ public void decrypt_other_key() throws Exception {
+ AesCipher cipher = new AesCipher(getPath("other_secret_key.txt"));
+
+ try {
+ // text encrypted with another key
+ cipher.decrypt("9mx5Zq4JVyjeChTcVjEide4kWCwusFl7P2dSVXtg9IY=");
+ fail();
+
+ } catch (RuntimeException e) {
+ assertThat(e.getCause()).isInstanceOf(BadPaddingException.class);
+ }
+ }
+
+ @Test
+ public void encryptThenDecrypt() throws Exception {
+ AesCipher cipher = new AesCipher(pathToSecretKey());
+
+ assertThat(cipher.decrypt(cipher.encrypt("foo"))).isEqualTo("foo");
+ }
+
+ @Test
+ public void testDefaultPathToSecretKey() {
+ AesCipher cipher = new AesCipher(null);
+
+ String path = cipher.getPathToSecretKey();
+
+ assertThat(StringUtils.isNotBlank(path)).isTrue();
+ assertThat(new File(path).getName()).isEqualTo("sonar-secret.txt");
+ }
+
+ @Test
+ public void loadSecretKeyFromFile() throws Exception {
+ AesCipher cipher = new AesCipher(null);
+ Key secretKey = cipher.loadSecretFileFromFile(pathToSecretKey());
+ assertThat(secretKey.getAlgorithm()).isEqualTo("AES");
+ assertThat(secretKey.getEncoded().length).isGreaterThan(10);
+ }
+
+ @Test
+ public void loadSecretKeyFromFile_trim_content() throws Exception {
+ String path = getPath("non_trimmed_secret_key.txt");
+ AesCipher cipher = new AesCipher(null);
+
+ Key secretKey = cipher.loadSecretFileFromFile(path);
+
+ assertThat(secretKey.getAlgorithm()).isEqualTo("AES");
+ assertThat(secretKey.getEncoded().length).isGreaterThan(10);
+ }
+
+ @Test
+ public void loadSecretKeyFromFile_file_does_not_exist() throws Exception {
+ thrown.expect(IllegalStateException.class);
+
+ AesCipher cipher = new AesCipher(null);
+ cipher.loadSecretFileFromFile("/file/does/not/exist");
+ }
+
+ @Test
+ public void loadSecretKeyFromFile_no_property() throws Exception {
+ thrown.expect(IllegalStateException.class);
+
+ AesCipher cipher = new AesCipher(null);
+ cipher.loadSecretFileFromFile(null);
+ }
+
+ @Test
+ public void hasSecretKey() throws Exception {
+ AesCipher cipher = new AesCipher(pathToSecretKey());
+
+ assertThat(cipher.hasSecretKey()).isTrue();
+ }
+
+ @Test
+ public void doesNotHaveSecretKey() throws Exception {
+ AesCipher cipher = new AesCipher("/my/twitter/id/is/SimonBrandhof");
+
+ assertThat(cipher.hasSecretKey()).isFalse();
+ }
+
+ private static String getPath(String file) {
+ return Resources.getResource(AesCipherTest.class, "AesCipherTest/" + file).getPath();
+ }
+
+ private static String pathToSecretKey() throws Exception {
+ return getPath("aes_secret_key.txt");
+ }
+
+}
diff --git a/server/process/sonar-process/src/test/java/org/sonar/process/ConfigurationUtilsTest.java b/server/process/sonar-process/src/test/java/org/sonar/process/ConfigurationUtilsTest.java
new file mode 100644
index 00000000000..6191eb2ba1f
--- /dev/null
+++ b/server/process/sonar-process/src/test/java/org/sonar/process/ConfigurationUtilsTest.java
@@ -0,0 +1,55 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.process;
+
+import com.google.common.collect.Maps;
+import org.junit.Test;
+
+import java.util.Map;
+import java.util.Properties;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+public class ConfigurationUtilsTest {
+ @Test
+ public void shouldInterpolateVariables() {
+ Properties input = new Properties();
+ input.setProperty("hello", "world");
+ input.setProperty("url", "${env:SONAR_JDBC_URL}");
+ input.setProperty("do_not_change", "${SONAR_JDBC_URL}");
+ Map<String, String> variables = Maps.newHashMap();
+ variables.put("SONAR_JDBC_URL", "jdbc:h2:mem");
+
+ Properties output = ConfigurationUtils.interpolateVariables(input, variables);
+
+ assertThat(output.size(), is(3));
+ assertThat(output.getProperty("hello"), is("world"));
+ assertThat(output.getProperty("url"), is("jdbc:h2:mem"));
+ assertThat(output.getProperty("do_not_change"), is("${SONAR_JDBC_URL}"));
+
+ // input is not changed
+ assertThat(input.size(), is(3));
+ assertThat(input.getProperty("hello"), is("world"));
+ assertThat(input.getProperty("url"), is("${env:SONAR_JDBC_URL}"));
+ assertThat(input.getProperty("do_not_change"), is("${SONAR_JDBC_URL}"));
+ }
+
+}
diff --git a/server/process/sonar-process/src/test/java/org/sonar/process/EncryptionTest.java b/server/process/sonar-process/src/test/java/org/sonar/process/EncryptionTest.java
new file mode 100644
index 00000000000..0c11856b0fa
--- /dev/null
+++ b/server/process/sonar-process/src/test/java/org/sonar/process/EncryptionTest.java
@@ -0,0 +1,59 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.process;
+
+import org.junit.Test;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+public class EncryptionTest {
+
+ @Test
+ public void isEncrypted() {
+ Encryption encryption = new Encryption(null);
+ assertThat(encryption.isEncrypted("{aes}ADASDASAD"), is(true));
+ assertThat(encryption.isEncrypted("{b64}ADASDASAD"), is(true));
+ assertThat(encryption.isEncrypted("{abc}ADASDASAD"), is(true));
+
+ assertThat(encryption.isEncrypted("{}"), is(false));
+ assertThat(encryption.isEncrypted("{foo"), is(false));
+ assertThat(encryption.isEncrypted("foo{aes}"), is(false));
+ }
+
+ @Test
+ public void decrypt() {
+ Encryption encryption = new Encryption(null);
+ assertThat(encryption.decrypt("{b64}Zm9v"), is("foo"));
+ }
+
+ @Test
+ public void decrypt_unknown_algorithm() {
+ Encryption encryption = new Encryption(null);
+ assertThat(encryption.decrypt("{xxx}Zm9v"), is("{xxx}Zm9v"));
+ }
+
+ @Test
+ public void decrypt_uncrypted_text() {
+ Encryption encryption = new Encryption(null);
+ assertThat(encryption.decrypt("foo"), is("foo"));
+ }
+}
diff --git a/server/process/sonar-process/src/test/java/org/sonar/process/MinimumViableSystemTest.java b/server/process/sonar-process/src/test/java/org/sonar/process/MinimumViableSystemTest.java
new file mode 100644
index 00000000000..11088902b14
--- /dev/null
+++ b/server/process/sonar-process/src/test/java/org/sonar/process/MinimumViableSystemTest.java
@@ -0,0 +1,79 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.process;
+
+import org.fest.assertions.Assertions;
+import org.junit.Test;
+
+import static org.fest.assertions.Fail.fail;
+
+public class MinimumViableSystemTest {
+
+ /**
+ * Verifies that all checks can be verified without error.
+ * Test environment does not necessarily follows all checks.
+ */
+ @Test
+ public void check() throws Exception {
+ MinimumViableSystem mve = new MinimumViableSystem();
+
+ try {
+ mve.check();
+ // ok
+ } catch (MessageException e) {
+ // also ok. All other exceptions are errors.
+ }
+ }
+
+ @Test
+ public void checkJavaVersion() throws Exception {
+ MinimumViableSystem mve = new MinimumViableSystem();
+
+ // yes, sources are compiled with a supported Java version!
+ mve.checkJavaVersion();
+
+ mve.checkJavaVersion("1.6.1_b2");
+ try {
+ mve.checkJavaVersion("1.5.2");
+ fail();
+ } catch (MessageException e) {
+ Assertions.assertThat(e).hasMessage("Minimal required Java version is 1.6. Got 1.5.2.");
+ }
+ }
+
+ @Test
+ public void checkJavaOption() throws Exception {
+ String key = "MinimumViableEnvironmentTest.test.prop";
+ MinimumViableSystem mve = new MinimumViableSystem()
+ .setRequiredJavaOption(key, "true");
+
+ try {
+ System.setProperty(key, "false");
+ mve.checkJavaOptions();
+ fail();
+ } catch (MessageException e) {
+ Assertions.assertThat(e).hasMessage("JVM option '" + key + "' must be set to 'true'. Got 'false'");
+ }
+
+ System.setProperty(key, "true");
+ mve.checkJavaOptions();
+ // do not fail
+ }
+}
diff --git a/server/process/sonar-process/src/test/java/org/sonar/process/NetworkUtilsTest.java b/server/process/sonar-process/src/test/java/org/sonar/process/NetworkUtilsTest.java
new file mode 100644
index 00000000000..09f6a597209
--- /dev/null
+++ b/server/process/sonar-process/src/test/java/org/sonar/process/NetworkUtilsTest.java
@@ -0,0 +1,61 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.process;
+
+import org.junit.Test;
+
+import java.net.ServerSocket;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class NetworkUtilsTest {
+
+
+ @Test
+ public void find_free_port() throws Exception {
+ int port = NetworkUtils.freePort();
+ assertThat(port).isGreaterThan(1024);
+ }
+
+ @Test
+ public void find_multiple_free_port() throws Exception {
+ int port1 = NetworkUtils.freePort();
+ int port2 = NetworkUtils.freePort();
+
+ assertThat(port1).isGreaterThan(1024);
+ assertThat(port2).isGreaterThan(1024);
+
+ assertThat(port1).isNotSameAs(port2);
+ }
+
+ @Test
+ public void find_multiple_free_non_adjacent_port() throws Exception {
+ int port1 = NetworkUtils.freePort();
+
+ ServerSocket socket = new ServerSocket(port1 + 1);
+
+ int port2 = NetworkUtils.freePort();
+
+ assertThat(port1).isGreaterThan(1024);
+ assertThat(port2).isGreaterThan(1024);
+
+ assertThat(port1).isNotSameAs(port2);
+ }
+} \ No newline at end of file
diff --git a/server/process/sonar-process/src/test/java/org/sonar/process/ProcessWrapperTest.java b/server/process/sonar-process/src/test/java/org/sonar/process/ProcessWrapperTest.java
new file mode 100644
index 00000000000..460983290b0
--- /dev/null
+++ b/server/process/sonar-process/src/test/java/org/sonar/process/ProcessWrapperTest.java
@@ -0,0 +1,68 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.process;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+
+public class ProcessWrapperTest {
+
+ int freePort;
+
+ @Before
+ public void setup() throws IOException {
+ ServerSocket socket = new ServerSocket(0);
+ freePort = socket.getLocalPort();
+ socket.close();
+ }
+
+
+ @Test
+ public void has_dummy_app(){
+
+ }
+
+
+// @Test
+// @Ignore("Not a good idea to assert on # of VMs")
+// public void process_should_run() throws IOException, MalformedObjectNameException, InterruptedException {
+//
+// LocalVirtualMachine.getAllVirtualMachines().size();
+// int VMcount = LocalVirtualMachine.getAllVirtualMachines().size();
+//
+// System.out.println("LocalVirtualMachine.getAllVirtualMachines() = " + LocalVirtualMachine.getAllVirtualMachines());
+//
+// RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
+// ProcessWrapper wrapper = wrapper = new ProcessWrapper(ProcessTest.TestProcess.class.getName(),
+// Collections.EMPTY_MAP, "TEST", freePort, runtime.getClassPath());
+//
+// assertThat(wrapper).isNotNull();
+// assertThat(wrapper.isReady()).isTrue();
+//
+// assertThat(LocalVirtualMachine.getAllVirtualMachines().size()).isEqualTo(VMcount + 1);
+//
+// wrapper.stop();
+// assertThat(LocalVirtualMachine.getAllVirtualMachines().size()).isEqualTo(VMcount);
+//
+// }
+} \ No newline at end of file
diff --git a/server/process/sonar-process/src/test/java/org/sonar/process/PropsTest.java b/server/process/sonar-process/src/test/java/org/sonar/process/PropsTest.java
new file mode 100644
index 00000000000..775d24a2a64
--- /dev/null
+++ b/server/process/sonar-process/src/test/java/org/sonar/process/PropsTest.java
@@ -0,0 +1,96 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.process;
+
+import org.junit.Test;
+
+import java.util.Properties;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.fest.assertions.Fail.fail;
+
+public class PropsTest {
+
+ @Test
+ public void of() throws Exception {
+ Properties p = new Properties();
+ p.setProperty("foo", "bar");
+ Props props = new Props(p);
+
+ assertThat(props.of("foo")).isEqualTo("bar");
+ assertThat(props.of("foo", "default value")).isEqualTo("bar");
+ assertThat(props.of("unknown")).isNull();
+ assertThat(props.of("unknown", "default value")).isEqualTo("default value");
+ }
+
+ @Test
+ public void intOf() throws Exception {
+ Properties p = new Properties();
+ p.setProperty("foo", "33");
+ p.setProperty("blank", "");
+ Props props = new Props(p);
+
+ assertThat(props.intOf("foo")).isEqualTo(33);
+ assertThat(props.intOf("foo", 44)).isEqualTo(33);
+ assertThat(props.intOf("blank")).isNull();
+ assertThat(props.intOf("blank", 55)).isEqualTo(55);
+ assertThat(props.intOf("unknown")).isNull();
+ assertThat(props.intOf("unknown", 44)).isEqualTo(44);
+ }
+
+ @Test
+ public void intOf_not_integer() throws Exception {
+ Properties p = new Properties();
+ p.setProperty("foo", "bar");
+ Props props = new Props(p);
+
+ try {
+ props.intOf("foo");
+ fail();
+ } catch (IllegalStateException e) {
+ assertThat(e).hasMessage("Value of property foo is not an integer: bar");
+ }
+ }
+
+ @Test
+ public void booleanOf() throws Exception {
+ Properties p = new Properties();
+ p.setProperty("foo", "True");
+ p.setProperty("bar", "false");
+ Props props = new Props(p);
+
+ assertThat(props.booleanOf("foo")).isTrue();
+ assertThat(props.booleanOf("bar")).isFalse();
+ assertThat(props.booleanOf("unknown")).isFalse();
+ }
+
+ @Test
+ public void booleanOf_default_value() throws Exception {
+ Properties p = new Properties();
+ p.setProperty("foo", "true");
+ p.setProperty("bar", "false");
+ Props props = new Props(p);
+
+ assertThat(props.booleanOf("unset", false)).isFalse();
+ assertThat(props.booleanOf("unset", true)).isTrue();
+ assertThat(props.booleanOf("foo", false)).isTrue();
+ assertThat(props.booleanOf("bar", true)).isFalse();
+ }
+}
diff --git a/server/process/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/aes_secret_key.txt b/server/process/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/aes_secret_key.txt
new file mode 100644
index 00000000000..65b98c522da
--- /dev/null
+++ b/server/process/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/aes_secret_key.txt
@@ -0,0 +1 @@
+0PZz+G+f8mjr3sPn4+AhHg== \ No newline at end of file
diff --git a/server/process/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/bad_secret_key.txt b/server/process/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/bad_secret_key.txt
new file mode 100644
index 00000000000..b33e179e5c8
--- /dev/null
+++ b/server/process/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/bad_secret_key.txt
@@ -0,0 +1 @@
+badbadbad== \ No newline at end of file
diff --git a/server/process/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/non_trimmed_secret_key.txt b/server/process/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/non_trimmed_secret_key.txt
new file mode 100644
index 00000000000..ab83e4adc03
--- /dev/null
+++ b/server/process/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/non_trimmed_secret_key.txt
@@ -0,0 +1,3 @@
+
+ 0PZz+G+f8mjr3sPn4+AhHg==
+
diff --git a/server/process/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/other_secret_key.txt b/server/process/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/other_secret_key.txt
new file mode 100644
index 00000000000..23f5ecf5104
--- /dev/null
+++ b/server/process/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/other_secret_key.txt
@@ -0,0 +1 @@
+IBxEUxZ41c8XTxyaah1Qlg== \ No newline at end of file
diff --git a/server/process/sonar-process/src/test/resources/org/sonar/process/LoggingTest/logback-access.xml b/server/process/sonar-process/src/test/resources/org/sonar/process/LoggingTest/logback-access.xml
new file mode 100644
index 00000000000..298193e01fa
--- /dev/null
+++ b/server/process/sonar-process/src/test/resources/org/sonar/process/LoggingTest/logback-access.xml
@@ -0,0 +1 @@
+<configuration/>
diff --git a/server/process/sonar-process/src/test/resources/org/sonar/process/ProcessTest/sonar.properties b/server/process/sonar-process/src/test/resources/org/sonar/process/ProcessTest/sonar.properties
new file mode 100644
index 00000000000..1577a214b3b
--- /dev/null
+++ b/server/process/sonar-process/src/test/resources/org/sonar/process/ProcessTest/sonar.properties
@@ -0,0 +1,212 @@
+# This file must contain only ISO 8859-1 characters
+# see http://docs.oracle.com/javase/1.5.0/docs/api/java/util/Properties.html#load(java.io.InputStream)
+#
+# To use an environment variable, use the following syntax : ${env:NAME_OF_ENV_VARIABLE}
+# For example:
+# sonar.jdbc.url= ${env:SONAR_JDBC_URL}
+#
+#
+# See also the file conf/wrapper.conf for JVM advanced settings
+
+
+
+#--------------------------------------------------------------------------------------------------
+# DATABASE
+#
+# IMPORTANT: the embedded H2 database is used by default. It is recommended for tests only.
+# Please use a production-ready database. Supported databases are MySQL, Oracle, PostgreSQL
+# and Microsoft SQLServer.
+
+# Permissions to create tables, indices and triggers must be granted to JDBC user.
+# The schema must be created first.
+sonar.jdbc.username=sonar
+sonar.jdbc.password=sonar
+
+#----- Embedded database H2
+# Note: it does not accept connections from remote hosts, so the
+# SonarQube server and the maven plugin must be executed on the same host.
+
+# Comment the following line to deactivate the default embedded database.
+sonar.jdbc.url=jdbc:h2:tcp://localhost:9092/sonar
+
+# directory containing H2 database files. By default it's the /data directory in the SonarQube installation.
+#sonar.embeddedDatabase.dataDir=
+# H2 embedded database server listening port, defaults to 9092
+#sonar.embeddedDatabase.port=9092
+
+
+#----- MySQL 5.x
+# Comment the embedded database and uncomment the following line to use MySQL
+#sonar.jdbc.url=jdbc:mysql://localhost:3306/sonar?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true
+
+
+#----- Oracle 10g/11g
+# To connect to Oracle database:
+#
+# - It's recommended to use the latest version of the JDBC driver (ojdbc6.jar).
+# Download it in http://www.oracle.com/technetwork/database/enterprise-edition/jdbc-112010-090769.html
+# - Copy the driver to the directory extensions/jdbc-driver/oracle/
+# - If you need to set the schema, please refer to http://jira.codehaus.org/browse/SONAR-5000
+# - Comment the embedded database and uncomment the following line:
+#sonar.jdbc.url=jdbc:oracle:thin:@localhost/XE
+
+
+#----- PostgreSQL 8.x/9.x
+# Comment the embedded database and uncomment the following property to use PostgreSQL.
+# If you don't use the schema named "public", please refer to http://jira.codehaus.org/browse/SONAR-5000
+#sonar.jdbc.url=jdbc:postgresql://localhost/sonar
+
+
+#----- Microsoft SQLServer
+# The Jtds open source driver is available in extensions/jdbc-driver/mssql. More details on http://jtds.sourceforge.net
+#sonar.jdbc.url=jdbc:jtds:sqlserver://localhost/sonar;SelectMethod=Cursor
+
+
+#----- Connection pool settings
+sonar.jdbc.maxActive=20
+sonar.jdbc.maxIdle=5
+sonar.jdbc.minIdle=2
+sonar.jdbc.maxWait=5000
+sonar.jdbc.minEvictableIdleTimeMillis=600000
+sonar.jdbc.timeBetweenEvictionRunsMillis=30000
+
+
+
+#--------------------------------------------------------------------------------------------------
+# WEB SERVER
+
+# Binding IP address. For servers with more than one IP address, this property specifies which
+# address will be used for listening on the specified ports.
+# By default, ports will be used on all IP addresses associated with the server.
+#sonar.web.host=0.0.0.0
+
+# Web context. When set, it must start with forward slash (for example /sonarqube).
+# The default value is root context (empty value).
+#sonar.web.context=
+
+# TCP port for incoming HTTP connections. Disabled when value is -1.
+#sonar.web.port=9000
+
+# TCP port for incoming HTTPS connections. Disabled when value is -1 (default).
+#sonar.web.https.port=-1
+
+# HTTPS - the alias used to for the server certificate in the keystore.
+# If not specified the first key read in the keystore is used.
+#sonar.web.https.keyAlias=
+
+# HTTPS - the password used to access the server certificate from the
+# specified keystore file. The default value is "changeit".
+#sonar.web.https.keyPass=changeit
+
+# HTTPS - the pathname of the keystore file where is stored the server certificate.
+# By default, the pathname is the file ".keystore" in the user home.
+# If keystoreType doesn't need a file use empty value.
+#sonar.web.https.keystoreFile=
+
+# HTTPS - the password used to access the specified keystore file. The default
+# value is the value of sonar.web.https.keyPass.
+#sonar.web.https.keystorePass=
+
+# HTTPS - the type of keystore file to be used for the server certificate.
+# The default value is JKS (Java KeyStore).
+#sonar.web.https.keystoreType=JKS
+
+# HTTPS - the name of the keystore provider to be used for the server certificate.
+# If not specified, the list of registered providers is traversed in preference order
+# and the first provider that supports the keystore type is used (see sonar.web.https.keystoreType).
+#sonar.web.https.keystoreProvider=
+
+# HTTPS - the pathname of the truststore file which contains trusted certificate authorities.
+# By default, this would be the cacerts file in your JRE.
+# If truststoreFile doesn't need a file use empty value.
+#sonar.web.https.truststoreFile=
+
+# HTTPS - the password used to access the specified truststore file.
+#sonar.web.https.truststorePass=
+
+# HTTPS - the type of truststore file to be used.
+# The default value is JKS (Java KeyStore).
+#sonar.web.https.truststoreType=JKS
+
+# HTTPS - the name of the truststore provider to be used for the server certificate.
+# If not specified, the list of registered providers is traversed in preference order
+# and the first provider that supports the truststore type is used (see sonar.web.https.truststoreType).
+#sonar.web.https.truststoreProvider=
+
+# HTTPS - whether to enable client certificate authentication.
+# The default is false (client certificates disabled).
+# Other possible values are 'want' (certificates will be requested, but not required),
+# and 'true' (certificates are required).
+#sonar.web.https.clientAuth=false
+
+# The maximum number of connections that the server will accept and process at any given time.
+# When this number has been reached, the server will not accept any more connections until
+# the number of connections falls below this value. The operating system may still accept connections
+# based on the sonar.web.connections.acceptCount property. The default value is 50 for each
+# enabled connector.
+#sonar.web.http.maxThreads=50
+#sonar.web.https.maxThreads=50
+
+# The minimum number of threads always kept running. The default value is 5 for each
+# enabled connector.
+#sonar.web.http.minThreads=5
+#sonar.web.https.minThreads=5
+
+# The maximum queue length for incoming connection requests when all possible request processing
+# threads are in use. Any requests received when the queue is full will be refused.
+# The default value is 25 for each enabled connector.
+#sonar.web.http.acceptCount=25
+#sonar.web.https.acceptCount=25
+
+# Access logs are generated in the file logs/access.log. This file is rolled over when it's 5Mb.
+# An archive of 3 files is kept in the same directory.
+# Access logs are enabled by default.
+#sonar.web.accessLogs.enable=true
+
+# TCP port for incoming AJP connections. Disabled when value is -1.
+# sonar.ajp.port=9009
+
+
+
+#--------------------------------------------------------------------------------------------------
+# UPDATE CENTER
+
+# The Update Center requires an internet connection to request http://update.sonarsource.org
+# It is enabled by default.
+#sonar.updatecenter.activate=true
+
+# HTTP proxy (default none)
+#http.proxyHost=
+#http.proxyPort=
+
+# NT domain name if NTLM proxy is used
+#http.auth.ntlm.domain=
+
+# SOCKS proxy (default none)
+#socksProxyHost=
+#socksProxyPort=
+
+# proxy authentication. The 2 following properties are used for HTTP and SOCKS proxies.
+#http.proxyUser=
+#http.proxyPassword=
+
+
+#--------------------------------------------------------------------------------------------------
+# NOTIFICATIONS
+
+# Delay in seconds between processing of notification queue. Default is 60.
+#sonar.notifications.delay=60
+
+
+#--------------------------------------------------------------------------------------------------
+# PROFILING
+# Level of information displayed in the logs: NONE (default), BASIC (functional information) and FULL (functional and technical details)
+#sonar.log.profilingLevel=NONE
+
+
+#--------------------------------------------------------------------------------------------------
+# DEVELOPMENT MODE
+# Only for debugging
+
+# Set to true to apply Ruby on Rails code changes on the fly
+#sonar.rails.dev=false
diff --git a/server/process/sonar-process/src/test/resources/org/sonar/process/PropsTest/sonar.properties b/server/process/sonar-process/src/test/resources/org/sonar/process/PropsTest/sonar.properties
new file mode 100644
index 00000000000..5c06e58a32e
--- /dev/null
+++ b/server/process/sonar-process/src/test/resources/org/sonar/process/PropsTest/sonar.properties
@@ -0,0 +1,3 @@
+hello: world
+foo=bar
+java.io.tmpdir=/should/be/overridden