You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

CommandFactoryImpl.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2021 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.application.command;
  21. import java.io.File;
  22. import java.util.Map;
  23. import java.util.Optional;
  24. import org.slf4j.LoggerFactory;
  25. import org.sonar.api.internal.MetadataLoader;
  26. import org.sonar.api.utils.Version;
  27. import org.sonar.application.es.EsInstallation;
  28. import org.sonar.application.es.EsLogging;
  29. import org.sonar.application.es.EsSettings;
  30. import org.sonar.application.es.EsYmlSettings;
  31. import org.sonar.process.ProcessId;
  32. import org.sonar.process.ProcessProperties;
  33. import org.sonar.process.Props;
  34. import org.sonar.process.System2;
  35. import static org.sonar.process.ProcessProperties.Property.CE_GRACEFUL_STOP_TIMEOUT;
  36. import static org.sonar.process.ProcessProperties.Property.CE_JAVA_ADDITIONAL_OPTS;
  37. import static org.sonar.process.ProcessProperties.Property.CE_JAVA_OPTS;
  38. import static org.sonar.process.ProcessProperties.Property.HTTPS_PROXY_HOST;
  39. import static org.sonar.process.ProcessProperties.Property.HTTPS_PROXY_PORT;
  40. import static org.sonar.process.ProcessProperties.Property.HTTP_AUTH_NTLM_DOMAIN;
  41. import static org.sonar.process.ProcessProperties.Property.HTTP_NON_PROXY_HOSTS;
  42. import static org.sonar.process.ProcessProperties.Property.HTTP_PROXY_HOST;
  43. import static org.sonar.process.ProcessProperties.Property.HTTP_PROXY_PORT;
  44. import static org.sonar.process.ProcessProperties.Property.JDBC_DRIVER_PATH;
  45. import static org.sonar.process.ProcessProperties.Property.PATH_HOME;
  46. import static org.sonar.process.ProcessProperties.Property.PATH_LOGS;
  47. import static org.sonar.process.ProcessProperties.Property.SEARCH_JAVA_ADDITIONAL_OPTS;
  48. import static org.sonar.process.ProcessProperties.Property.SEARCH_JAVA_OPTS;
  49. import static org.sonar.process.ProcessProperties.Property.SOCKS_PROXY_HOST;
  50. import static org.sonar.process.ProcessProperties.Property.SOCKS_PROXY_PORT;
  51. import static org.sonar.process.ProcessProperties.Property.WEB_GRACEFUL_STOP_TIMEOUT;
  52. import static org.sonar.process.ProcessProperties.Property.WEB_JAVA_ADDITIONAL_OPTS;
  53. import static org.sonar.process.ProcessProperties.Property.WEB_JAVA_OPTS;
  54. import static org.sonar.process.ProcessProperties.parseTimeoutMs;
  55. public class CommandFactoryImpl implements CommandFactory {
  56. private static final String ENV_VAR_JAVA_TOOL_OPTIONS = "JAVA_TOOL_OPTIONS";
  57. private static final String ENV_VAR_ES_JAVA_OPTS = "ES_JAVA_OPTS";
  58. /**
  59. * Properties about proxy that must be set as system properties
  60. */
  61. private static final String[] PROXY_PROPERTY_KEYS = new String[] {
  62. HTTP_PROXY_HOST.getKey(),
  63. HTTP_PROXY_PORT.getKey(),
  64. HTTP_NON_PROXY_HOSTS.getKey(),
  65. HTTPS_PROXY_HOST.getKey(),
  66. HTTPS_PROXY_PORT.getKey(),
  67. HTTP_AUTH_NTLM_DOMAIN.getKey(),
  68. SOCKS_PROXY_HOST.getKey(),
  69. SOCKS_PROXY_PORT.getKey()};
  70. private static final Version SQ_VERSION = MetadataLoader.loadVersion(org.sonar.api.utils.System2.INSTANCE);
  71. private final Props props;
  72. private final File tempDir;
  73. private final System2 system2;
  74. private final JavaVersion javaVersion;
  75. public CommandFactoryImpl(Props props, File tempDir, System2 system2, JavaVersion javaVersion) {
  76. this.props = props;
  77. this.tempDir = tempDir;
  78. this.system2 = system2;
  79. this.javaVersion = javaVersion;
  80. String javaToolOptions = system2.getenv(ENV_VAR_JAVA_TOOL_OPTIONS);
  81. if (javaToolOptions != null && !javaToolOptions.trim().isEmpty()) {
  82. LoggerFactory.getLogger(CommandFactoryImpl.class)
  83. .warn("JAVA_TOOL_OPTIONS is defined but will be ignored. " +
  84. "Use properties sonar.*.javaOpts and/or sonar.*.javaAdditionalOpts in sonar.properties to change SQ JVM processes options");
  85. }
  86. String esJavaOpts = system2.getenv(ENV_VAR_ES_JAVA_OPTS);
  87. if (esJavaOpts != null && !esJavaOpts.trim().isEmpty()) {
  88. LoggerFactory.getLogger(CommandFactoryImpl.class)
  89. .warn("ES_JAVA_OPTS is defined but will be ignored. " +
  90. "Use properties sonar.search.javaOpts and/or sonar.search.javaAdditionalOpts in sonar.properties to change SQ JVM processes options");
  91. }
  92. }
  93. @Override
  94. public AbstractCommand<?> createEsCommand() {
  95. if (system2.isOsWindows()) {
  96. return createEsCommandForWindows();
  97. }
  98. return createEsCommandForUnix();
  99. }
  100. private EsScriptCommand createEsCommandForUnix() {
  101. EsInstallation esInstallation = createEsInstallation();
  102. return new EsScriptCommand(ProcessId.ELASTICSEARCH, esInstallation.getHomeDirectory())
  103. .setEsInstallation(esInstallation)
  104. .setEnvVariable("ES_PATH_CONF", esInstallation.getConfDirectory().getAbsolutePath())
  105. .setEnvVariable("ES_JVM_OPTIONS", esInstallation.getJvmOptions().getAbsolutePath())
  106. .setEnvVariable("JAVA_HOME", System.getProperties().getProperty("java.home"))
  107. .suppressEnvVariable(ENV_VAR_JAVA_TOOL_OPTIONS)
  108. .suppressEnvVariable(ENV_VAR_ES_JAVA_OPTS);
  109. }
  110. private JavaCommand createEsCommandForWindows() {
  111. EsInstallation esInstallation = createEsInstallation();
  112. return new JavaCommand<EsJvmOptions>(ProcessId.ELASTICSEARCH, esInstallation.getHomeDirectory())
  113. .setEsInstallation(esInstallation)
  114. .setReadsArgumentsFromFile(false)
  115. .setJvmOptions(esInstallation.getEsJvmOptions()
  116. .add("-Delasticsearch")
  117. .add("-Des.path.home=" + esInstallation.getHomeDirectory().getAbsolutePath())
  118. .add("-Des.path.conf=" + esInstallation.getConfDirectory().getAbsolutePath()))
  119. .setEnvVariable("ES_JVM_OPTIONS", esInstallation.getJvmOptions().getAbsolutePath())
  120. .setEnvVariable("JAVA_HOME", System.getProperties().getProperty("java.home"))
  121. .setClassName("org.elasticsearch.bootstrap.Elasticsearch")
  122. .addClasspath("lib/*")
  123. .suppressEnvVariable(ENV_VAR_JAVA_TOOL_OPTIONS)
  124. .suppressEnvVariable(ENV_VAR_ES_JAVA_OPTS);
  125. }
  126. private EsInstallation createEsInstallation() {
  127. EsInstallation esInstallation = new EsInstallation(props);
  128. if (!esInstallation.getExecutable().exists()) {
  129. throw new IllegalStateException("Cannot find elasticsearch binary");
  130. }
  131. Map<String, String> settingsMap = new EsSettings(props, esInstallation, System2.INSTANCE).build();
  132. esInstallation
  133. .setLog4j2Properties(new EsLogging().createProperties(props, esInstallation.getLogDirectory()))
  134. .setEsJvmOptions(new EsJvmOptions(props, tempDir)
  135. .addFromMandatoryProperty(props, SEARCH_JAVA_OPTS.getKey())
  136. .addFromMandatoryProperty(props, SEARCH_JAVA_ADDITIONAL_OPTS.getKey()))
  137. .setEsYmlSettings(new EsYmlSettings(settingsMap))
  138. .setHost(settingsMap.get("http.host"))
  139. .setHttpPort(Integer.parseInt(settingsMap.get("http.port")));
  140. return esInstallation;
  141. }
  142. @Override
  143. public JavaCommand createWebCommand(boolean leader) {
  144. File homeDir = props.nonNullValueAsFile(PATH_HOME.getKey());
  145. WebJvmOptions jvmOptions = new WebJvmOptions(tempDir, javaVersion)
  146. .addFromMandatoryProperty(props, WEB_JAVA_OPTS.getKey())
  147. .addFromMandatoryProperty(props, WEB_JAVA_ADDITIONAL_OPTS.getKey());
  148. addProxyJvmOptions(jvmOptions);
  149. JavaCommand<WebJvmOptions> command = new JavaCommand<WebJvmOptions>(ProcessId.WEB_SERVER, homeDir)
  150. .setReadsArgumentsFromFile(true)
  151. .setArguments(props.rawProperties())
  152. .setJvmOptions(jvmOptions)
  153. .setGracefulStopTimeoutMs(getGracefulStopTimeoutMs(props, WEB_GRACEFUL_STOP_TIMEOUT))
  154. // required for logback tomcat valve
  155. .setEnvVariable(PATH_LOGS.getKey(), props.nonNullValue(PATH_LOGS.getKey()))
  156. .setArgument("sonar.cluster.web.startupLeader", Boolean.toString(leader))
  157. .setClassName("org.sonar.server.app.WebServer")
  158. .addClasspath("./lib/sonar-application-" + SQ_VERSION + ".jar");
  159. String driverPath = props.value(JDBC_DRIVER_PATH.getKey());
  160. if (driverPath != null) {
  161. command.addClasspath(driverPath);
  162. }
  163. command.suppressEnvVariable(ENV_VAR_JAVA_TOOL_OPTIONS);
  164. return command;
  165. }
  166. @Override
  167. public JavaCommand createCeCommand() {
  168. File homeDir = props.nonNullValueAsFile(PATH_HOME.getKey());
  169. CeJvmOptions jvmOptions = new CeJvmOptions(tempDir, javaVersion)
  170. .addFromMandatoryProperty(props, CE_JAVA_OPTS.getKey())
  171. .addFromMandatoryProperty(props, CE_JAVA_ADDITIONAL_OPTS.getKey());
  172. addProxyJvmOptions(jvmOptions);
  173. JavaCommand<CeJvmOptions> command = new JavaCommand<CeJvmOptions>(ProcessId.COMPUTE_ENGINE, homeDir)
  174. .setReadsArgumentsFromFile(true)
  175. .setArguments(props.rawProperties())
  176. .setJvmOptions(jvmOptions)
  177. .setGracefulStopTimeoutMs(getGracefulStopTimeoutMs(props, CE_GRACEFUL_STOP_TIMEOUT))
  178. .setClassName("org.sonar.ce.app.CeServer")
  179. .addClasspath("./lib/sonar-application-" + SQ_VERSION + ".jar");
  180. String driverPath = props.value(JDBC_DRIVER_PATH.getKey());
  181. if (driverPath != null) {
  182. command.addClasspath(driverPath);
  183. }
  184. command.suppressEnvVariable(ENV_VAR_JAVA_TOOL_OPTIONS);
  185. return command;
  186. }
  187. private static long getGracefulStopTimeoutMs(Props props, ProcessProperties.Property property) {
  188. String value = Optional.ofNullable(props.value(property.getKey()))
  189. .orElse(property.getDefaultValue());
  190. // give some time to CE/Web to shutdown itself after graceful stop timed out
  191. long gracePeriod = 30 * 1_000L;
  192. return parseTimeoutMs(property, value) + gracePeriod;
  193. }
  194. private <T extends JvmOptions> void addProxyJvmOptions(JvmOptions<T> jvmOptions) {
  195. for (String key : PROXY_PROPERTY_KEYS) {
  196. getPropsValue(key).ifPresent(val -> jvmOptions.add("-D" + key + "=" + val));
  197. }
  198. // defaults of HTTPS are the same than HTTP defaults
  199. setSystemPropertyToDefaultIfNotSet(jvmOptions, HTTPS_PROXY_HOST.getKey(), HTTP_PROXY_HOST.getKey());
  200. setSystemPropertyToDefaultIfNotSet(jvmOptions, HTTPS_PROXY_PORT.getKey(), HTTP_PROXY_PORT.getKey());
  201. }
  202. private void setSystemPropertyToDefaultIfNotSet(JvmOptions jvmOptions,
  203. String httpsProperty, String httpProperty) {
  204. Optional<String> httpValue = getPropsValue(httpProperty);
  205. Optional<String> httpsValue = getPropsValue(httpsProperty);
  206. if (!httpsValue.isPresent() && httpValue.isPresent()) {
  207. jvmOptions.add("-D" + httpsProperty + "=" + httpValue.get());
  208. }
  209. }
  210. private Optional<String> getPropsValue(String key) {
  211. return Optional.ofNullable(props.value(key));
  212. }
  213. }