From 17a40828946cf3c133b3d8d3ed441c4697bd3229 Mon Sep 17 00:00:00 2001 From: Stephane Gamard Date: Fri, 8 Aug 2014 11:27:03 +0200 Subject: [PATCH] SONAR-4898 - Modularized sonar-process for tests --- server/pom.xml | 3 +- server/process/pom.xml | 22 ++ .../sonar-dummy-app}/pom.xml | 11 +- .../org/sonar/application/DummyOkApp.java | 0 .../org/sonar/application/logback.xml | 0 .../sonar-process-test}/pom.xml | 22 +- .../java/org/sonar/process/AesCipher.java | 0 .../java/org/sonar/process/Base64Cipher.java | 0 .../main/java/org/sonar/process/Cipher.java | 0 .../org/sonar/process/ConfigurationUtils.java | 0 .../java/org/sonar/process/Encryption.java | 0 .../main/java/org/sonar/process/JmxUtils.java | 0 .../org/sonar/process/MessageException.java | 0 .../sonar/process/MinimumViableSystem.java | 0 .../main/java/org/sonar/process/Monitor.java | 0 .../org/sonar/process/MonitoredProcess.java | 0 .../java/org/sonar/process/NetworkUtils.java | 0 .../org/sonar/process/ProcessLogging.java | 0 .../java/org/sonar/process/ProcessMXBean.java | 0 .../java/org/sonar/process/ProcessUtils.java | 0 .../org/sonar/process/ProcessWrapper.java | 0 .../main/java/org/sonar/process/Props.java | 0 .../java/org/sonar/process/Terminable.java | 0 .../java/org/sonar/process/AesCipherTest.java | 0 .../sonar/process/ConfigurationUtilsTest.java | 0 .../org/sonar/process/EncryptionTest.java | 0 .../process/MinimumViableSystemTest.java | 0 .../org/sonar/process/NetworkUtilsTest.java | 0 .../org/sonar/process/ProcessWrapperTest.java | 0 .../java/org/sonar/process/PropsTest.java | 0 .../process/AesCipherTest/aes_secret_key.txt | 0 .../process/AesCipherTest/bad_secret_key.txt | 0 .../AesCipherTest/non_trimmed_secret_key.txt | 0 .../AesCipherTest/other_secret_key.txt | 0 .../process/LoggingTest/logback-access.xml | 0 .../process/ProcessTest/sonar.properties | 0 .../sonar/process/PropsTest/sonar.properties | 0 server/process/sonar-process/pom.xml | 72 ++++ .../java/org/sonar/process/AesCipher.java | 137 +++++++ .../java/org/sonar/process/Base64Cipher.java | 35 ++ .../main/java/org/sonar/process/Cipher.java | 27 ++ .../org/sonar/process/ConfigurationUtils.java | 75 ++++ .../java/org/sonar/process/Encryption.java | 64 ++++ .../main/java/org/sonar/process/JmxUtils.java | 54 +++ .../org/sonar/process/MessageException.java | 36 ++ .../sonar/process/MinimumViableSystem.java | 84 +++++ .../main/java/org/sonar/process/Monitor.java | 122 +++++++ .../org/sonar/process/MonitoredProcess.java | 131 +++++++ .../java/org/sonar/process/NetworkUtils.java | 37 ++ .../org/sonar/process/ProcessLogging.java | 53 +++ .../java/org/sonar/process/ProcessMXBean.java | 27 ++ .../java/org/sonar/process/ProcessUtils.java | 70 ++++ .../org/sonar/process/ProcessWrapper.java | 345 ++++++++++++++++++ .../main/java/org/sonar/process/Props.java | 106 ++++++ .../java/org/sonar/process/Terminable.java | 30 ++ .../java/org/sonar/process/AesCipherTest.java | 185 ++++++++++ .../sonar/process/ConfigurationUtilsTest.java | 55 +++ .../org/sonar/process/EncryptionTest.java | 59 +++ .../process/MinimumViableSystemTest.java | 79 ++++ .../org/sonar/process/NetworkUtilsTest.java | 61 ++++ .../org/sonar/process/ProcessWrapperTest.java | 68 ++++ .../java/org/sonar/process/PropsTest.java | 96 +++++ .../process/AesCipherTest/aes_secret_key.txt | 1 + .../process/AesCipherTest/bad_secret_key.txt | 1 + .../AesCipherTest/non_trimmed_secret_key.txt | 3 + .../AesCipherTest/other_secret_key.txt | 1 + .../process/LoggingTest/logback-access.xml | 1 + .../process/ProcessTest/sonar.properties | 212 +++++++++++ .../sonar/process/PropsTest/sonar.properties | 3 + 69 files changed, 2375 insertions(+), 13 deletions(-) create mode 100644 server/process/pom.xml rename server/{sonar-process-test => process/sonar-dummy-app}/pom.xml (89%) rename server/{sonar-process-test => process/sonar-dummy-app}/src/main/java/org/sonar/application/DummyOkApp.java (100%) rename server/{sonar-process-test => process/sonar-dummy-app}/src/main/resources/org/sonar/application/logback.xml (100%) rename server/{sonar-process => process/sonar-process-test}/pom.xml (82%) rename server/{sonar-process => process/sonar-process-test}/src/main/java/org/sonar/process/AesCipher.java (100%) rename server/{sonar-process => process/sonar-process-test}/src/main/java/org/sonar/process/Base64Cipher.java (100%) rename server/{sonar-process => process/sonar-process-test}/src/main/java/org/sonar/process/Cipher.java (100%) rename server/{sonar-process => process/sonar-process-test}/src/main/java/org/sonar/process/ConfigurationUtils.java (100%) rename server/{sonar-process => process/sonar-process-test}/src/main/java/org/sonar/process/Encryption.java (100%) rename server/{sonar-process => process/sonar-process-test}/src/main/java/org/sonar/process/JmxUtils.java (100%) rename server/{sonar-process => process/sonar-process-test}/src/main/java/org/sonar/process/MessageException.java (100%) rename server/{sonar-process => process/sonar-process-test}/src/main/java/org/sonar/process/MinimumViableSystem.java (100%) rename server/{sonar-process => process/sonar-process-test}/src/main/java/org/sonar/process/Monitor.java (100%) rename server/{sonar-process => process/sonar-process-test}/src/main/java/org/sonar/process/MonitoredProcess.java (100%) rename server/{sonar-process => process/sonar-process-test}/src/main/java/org/sonar/process/NetworkUtils.java (100%) rename server/{sonar-process => process/sonar-process-test}/src/main/java/org/sonar/process/ProcessLogging.java (100%) rename server/{sonar-process => process/sonar-process-test}/src/main/java/org/sonar/process/ProcessMXBean.java (100%) rename server/{sonar-process => process/sonar-process-test}/src/main/java/org/sonar/process/ProcessUtils.java (100%) rename server/{sonar-process => process/sonar-process-test}/src/main/java/org/sonar/process/ProcessWrapper.java (100%) rename server/{sonar-process => process/sonar-process-test}/src/main/java/org/sonar/process/Props.java (100%) rename server/{sonar-process => process/sonar-process-test}/src/main/java/org/sonar/process/Terminable.java (100%) rename server/{sonar-process => process/sonar-process-test}/src/test/java/org/sonar/process/AesCipherTest.java (100%) rename server/{sonar-process => process/sonar-process-test}/src/test/java/org/sonar/process/ConfigurationUtilsTest.java (100%) rename server/{sonar-process => process/sonar-process-test}/src/test/java/org/sonar/process/EncryptionTest.java (100%) rename server/{sonar-process => process/sonar-process-test}/src/test/java/org/sonar/process/MinimumViableSystemTest.java (100%) rename server/{sonar-process => process/sonar-process-test}/src/test/java/org/sonar/process/NetworkUtilsTest.java (100%) rename server/{sonar-process => process/sonar-process-test}/src/test/java/org/sonar/process/ProcessWrapperTest.java (100%) rename server/{sonar-process => process/sonar-process-test}/src/test/java/org/sonar/process/PropsTest.java (100%) rename server/{sonar-process => process/sonar-process-test}/src/test/resources/org/sonar/process/AesCipherTest/aes_secret_key.txt (100%) rename server/{sonar-process => process/sonar-process-test}/src/test/resources/org/sonar/process/AesCipherTest/bad_secret_key.txt (100%) rename server/{sonar-process => process/sonar-process-test}/src/test/resources/org/sonar/process/AesCipherTest/non_trimmed_secret_key.txt (100%) rename server/{sonar-process => process/sonar-process-test}/src/test/resources/org/sonar/process/AesCipherTest/other_secret_key.txt (100%) rename server/{sonar-process => process/sonar-process-test}/src/test/resources/org/sonar/process/LoggingTest/logback-access.xml (100%) rename server/{sonar-process => process/sonar-process-test}/src/test/resources/org/sonar/process/ProcessTest/sonar.properties (100%) rename server/{sonar-process => process/sonar-process-test}/src/test/resources/org/sonar/process/PropsTest/sonar.properties (100%) create mode 100644 server/process/sonar-process/pom.xml create mode 100644 server/process/sonar-process/src/main/java/org/sonar/process/AesCipher.java create mode 100644 server/process/sonar-process/src/main/java/org/sonar/process/Base64Cipher.java create mode 100644 server/process/sonar-process/src/main/java/org/sonar/process/Cipher.java create mode 100644 server/process/sonar-process/src/main/java/org/sonar/process/ConfigurationUtils.java create mode 100644 server/process/sonar-process/src/main/java/org/sonar/process/Encryption.java create mode 100644 server/process/sonar-process/src/main/java/org/sonar/process/JmxUtils.java create mode 100644 server/process/sonar-process/src/main/java/org/sonar/process/MessageException.java create mode 100644 server/process/sonar-process/src/main/java/org/sonar/process/MinimumViableSystem.java create mode 100644 server/process/sonar-process/src/main/java/org/sonar/process/Monitor.java create mode 100644 server/process/sonar-process/src/main/java/org/sonar/process/MonitoredProcess.java create mode 100644 server/process/sonar-process/src/main/java/org/sonar/process/NetworkUtils.java create mode 100644 server/process/sonar-process/src/main/java/org/sonar/process/ProcessLogging.java create mode 100644 server/process/sonar-process/src/main/java/org/sonar/process/ProcessMXBean.java create mode 100644 server/process/sonar-process/src/main/java/org/sonar/process/ProcessUtils.java create mode 100644 server/process/sonar-process/src/main/java/org/sonar/process/ProcessWrapper.java create mode 100644 server/process/sonar-process/src/main/java/org/sonar/process/Props.java create mode 100644 server/process/sonar-process/src/main/java/org/sonar/process/Terminable.java create mode 100644 server/process/sonar-process/src/test/java/org/sonar/process/AesCipherTest.java create mode 100644 server/process/sonar-process/src/test/java/org/sonar/process/ConfigurationUtilsTest.java create mode 100644 server/process/sonar-process/src/test/java/org/sonar/process/EncryptionTest.java create mode 100644 server/process/sonar-process/src/test/java/org/sonar/process/MinimumViableSystemTest.java create mode 100644 server/process/sonar-process/src/test/java/org/sonar/process/NetworkUtilsTest.java create mode 100644 server/process/sonar-process/src/test/java/org/sonar/process/ProcessWrapperTest.java create mode 100644 server/process/sonar-process/src/test/java/org/sonar/process/PropsTest.java create mode 100644 server/process/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/aes_secret_key.txt create mode 100644 server/process/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/bad_secret_key.txt create mode 100644 server/process/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/non_trimmed_secret_key.txt create mode 100644 server/process/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/other_secret_key.txt create mode 100644 server/process/sonar-process/src/test/resources/org/sonar/process/LoggingTest/logback-access.xml create mode 100644 server/process/sonar-process/src/test/resources/org/sonar/process/ProcessTest/sonar.properties create mode 100644 server/process/sonar-process/src/test/resources/org/sonar/process/PropsTest/sonar.properties 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 @@ SonarQube :: Server :: Parent - sonar-process-test - sonar-process + process sonar-search sonar-server sonar-web 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 @@ + + + + org.codehaus.sonar + server + 4.5-SNAPSHOT + ../ + + 4.0.0 + + process + pom + SonarQube :: Process :: Parent + + + sonar-dummy-app + sonar-process + sonar-process-test + + diff --git a/server/sonar-process-test/pom.xml b/server/process/sonar-dummy-app/pom.xml similarity index 89% rename from server/sonar-process-test/pom.xml rename to 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"> org.codehaus.sonar - server + process 4.5-SNAPSHOT ../ 4.0.0 - sonar-process-test + sonar-dummy-app jar - SonarQube :: Process :: Test - Wrapper to start Elasticsearch + SonarQube :: Process :: DummyApp + Dummy Application to test sonar-process org.codehaus.sonar sonar-process ${project.version} - provided com.google.code.findbugs @@ -86,7 +85,7 @@ shade - true + false true 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 similarity index 100% rename from server/sonar-process-test/src/main/java/org/sonar/application/DummyOkApp.java rename to 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 similarity index 100% rename from server/sonar-process-test/src/main/resources/org/sonar/application/logback.xml rename to 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 similarity index 82% rename from server/sonar-process/pom.xml rename to 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"> org.codehaus.sonar - server + process 4.5-SNAPSHOT - .. + ../ 4.0.0 - sonar-process + sonar-process-test jar - SonarQube :: Process + SonarQube :: Process :: Tests @@ -68,6 +68,18 @@ guava test + + + org.codehaus.sonar + sonar-dummy-app + ${project.version} + + + org.codehaus.sonar + sonar-process + ${project.version} + test + @@ -86,7 +98,7 @@ org.codehaus.sonar - sonar-process-test + sonar-dummy-app ${project.version} jar ${project.build.directory}/dummyApp 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 similarity index 100% rename from server/sonar-process/src/main/java/org/sonar/process/AesCipher.java rename to 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 similarity index 100% rename from server/sonar-process/src/main/java/org/sonar/process/Base64Cipher.java rename to 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 similarity index 100% rename from server/sonar-process/src/main/java/org/sonar/process/Cipher.java rename to 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 similarity index 100% rename from server/sonar-process/src/main/java/org/sonar/process/ConfigurationUtils.java rename to 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 similarity index 100% rename from server/sonar-process/src/main/java/org/sonar/process/Encryption.java rename to 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 similarity index 100% rename from server/sonar-process/src/main/java/org/sonar/process/JmxUtils.java rename to 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 similarity index 100% rename from server/sonar-process/src/main/java/org/sonar/process/MessageException.java rename to 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 similarity index 100% rename from server/sonar-process/src/main/java/org/sonar/process/MinimumViableSystem.java rename to 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 similarity index 100% rename from server/sonar-process/src/main/java/org/sonar/process/Monitor.java rename to 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 similarity index 100% rename from server/sonar-process/src/main/java/org/sonar/process/MonitoredProcess.java rename to 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 similarity index 100% rename from server/sonar-process/src/main/java/org/sonar/process/NetworkUtils.java rename to 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 similarity index 100% rename from server/sonar-process/src/main/java/org/sonar/process/ProcessLogging.java rename to 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 similarity index 100% rename from server/sonar-process/src/main/java/org/sonar/process/ProcessMXBean.java rename to 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 similarity index 100% rename from server/sonar-process/src/main/java/org/sonar/process/ProcessUtils.java rename to 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 similarity index 100% rename from server/sonar-process/src/main/java/org/sonar/process/ProcessWrapper.java rename to 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 similarity index 100% rename from server/sonar-process/src/main/java/org/sonar/process/Props.java rename to 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 similarity index 100% rename from server/sonar-process/src/main/java/org/sonar/process/Terminable.java rename to 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 similarity index 100% rename from server/sonar-process/src/test/java/org/sonar/process/AesCipherTest.java rename to 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 similarity index 100% rename from server/sonar-process/src/test/java/org/sonar/process/ConfigurationUtilsTest.java rename to 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 similarity index 100% rename from server/sonar-process/src/test/java/org/sonar/process/EncryptionTest.java rename to 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 similarity index 100% rename from server/sonar-process/src/test/java/org/sonar/process/MinimumViableSystemTest.java rename to 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 similarity index 100% rename from server/sonar-process/src/test/java/org/sonar/process/NetworkUtilsTest.java rename to 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 similarity index 100% rename from server/sonar-process/src/test/java/org/sonar/process/ProcessWrapperTest.java rename to 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 similarity index 100% rename from server/sonar-process/src/test/java/org/sonar/process/PropsTest.java rename to 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 similarity index 100% rename from server/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/aes_secret_key.txt rename to 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 similarity index 100% rename from server/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/bad_secret_key.txt rename to 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 similarity index 100% rename from server/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/non_trimmed_secret_key.txt rename to 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 similarity index 100% rename from server/sonar-process/src/test/resources/org/sonar/process/AesCipherTest/other_secret_key.txt rename to 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 similarity index 100% rename from server/sonar-process/src/test/resources/org/sonar/process/LoggingTest/logback-access.xml rename to 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 similarity index 100% rename from server/sonar-process/src/test/resources/org/sonar/process/ProcessTest/sonar.properties rename to 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 similarity index 100% rename from server/sonar-process/src/test/resources/org/sonar/process/PropsTest/sonar.properties rename to 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 @@ + + + + org.codehaus.sonar + process + 4.5-SNAPSHOT + ../ + + 4.0.0 + + sonar-process + jar + SonarQube :: Process + + + + + org.slf4j + slf4j-api + + + ch.qos.logback + logback-classic + + + + commons-codec + commons-codec + + + commons-io + commons-io + + + commons-lang + commons-lang + + + com.google.code.findbugs + jsr305 + provided + + + + junit + junit + test + + + org.easytesting + fest-assert + test + + + org.mockito + mockito-core + test + + + org.hamcrest + hamcrest-all + test + + + com.google.guava + guava + test + + + 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 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 ciphers = new HashMap(); + 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 requiredJavaOptions = new HashMap(); + + 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 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 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(); + 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 javaOpts = new ArrayList(); + private final List classpath = new ArrayList(); + private final Map envProperties = new HashMap(); + 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 command = new ArrayList(); + 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 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 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 null + */ + @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 not 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 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 @@ + 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 -- 2.39.5