import org.slf4j.LoggerFactory;
import org.sonar.application.config.AppSettings;
import org.sonar.application.config.ClusterSettings;
-import org.sonar.application.process.CommandFactory;
-import org.sonar.application.process.EsCommand;
-import org.sonar.application.process.JavaCommand;
+import org.sonar.process.command.CommandFactory;
+import org.sonar.process.command.EsCommand;
+import org.sonar.process.command.JavaCommand;
import org.sonar.application.process.ProcessLauncher;
import org.sonar.application.process.Lifecycle;
import org.sonar.application.process.ProcessEventListener;
private void tryToStartEs() {
SQProcess process = processesById.get(ProcessId.ELASTICSEARCH);
if (process != null) {
- tryToStartEsProcess(process, () -> commandFactory.createEsCommand(settings));
+ tryToStartEsProcess(process, commandFactory::createEsCommand);
}
}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.application.process;
-
-import java.io.File;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Properties;
-import javax.annotation.Nullable;
-import org.sonar.process.ProcessId;
-
-public abstract class AbstractCommand<T extends AbstractCommand> {
- // unique key among the group of commands to launch
- private final ProcessId id;
- // program arguments
- private final Map<String, String> arguments = new LinkedHashMap<>();
- private final Map<String, String> envVariables = new HashMap<>(System.getenv());
- private File workDir;
-
- protected AbstractCommand(ProcessId id) {
- this.id = id;
- }
-
- public ProcessId getProcessId() {
- return id;
- }
-
- public File getWorkDir() {
- return workDir;
- }
-
- public T setWorkDir(File workDir) {
- this.workDir = workDir;
- return castThis();
- }
-
- @SuppressWarnings("unchecked")
- private T castThis() {
- return (T) this;
- }
-
- public Map<String, String> getArguments() {
- return arguments;
- }
-
- public T setArgument(String key, @Nullable String value) {
- if (value == null) {
- arguments.remove(key);
- } else {
- arguments.put(key, value);
- }
- return castThis();
- }
-
- public T setArguments(Properties args) {
- for (Map.Entry<Object, Object> entry : args.entrySet()) {
- setArgument(entry.getKey().toString(), entry.getValue() != null ? entry.getValue().toString() : null);
- }
- return castThis();
- }
-
- public Map<String, String> getEnvVariables() {
- return envVariables;
- }
-
- public T setEnvVariable(String key, @Nullable String value) {
- if (value == null) {
- envVariables.remove(key);
- } else {
- envVariables.put(key, value);
- }
- return castThis();
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.application.process;
-
-import org.sonar.application.config.AppSettings;
-
-public interface CommandFactory {
-
- EsCommand createEsCommand(AppSettings settings);
-
- JavaCommand createWebCommand(boolean leader);
-
- JavaCommand createCeCommand();
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.application.process;
-
-import java.io.File;
-import java.util.Map;
-import java.util.Optional;
-import org.sonar.application.config.AppSettings;
-import org.sonar.process.ProcessId;
-import org.sonar.process.ProcessProperties;
-
-import static org.sonar.process.ProcessProperties.HTTPS_PROXY_HOST;
-import static org.sonar.process.ProcessProperties.HTTPS_PROXY_PORT;
-import static org.sonar.process.ProcessProperties.HTTP_PROXY_HOST;
-import static org.sonar.process.ProcessProperties.HTTP_PROXY_PORT;
-
-public class CommandFactoryImpl implements CommandFactory {
- /**
- * Properties about proxy that must be set as system properties
- */
- private static final String[] PROXY_PROPERTY_KEYS = new String[] {
- HTTP_PROXY_HOST,
- HTTP_PROXY_PORT,
- "http.nonProxyHosts",
- HTTPS_PROXY_HOST,
- HTTPS_PROXY_PORT,
- "http.auth.ntlm.domain",
- "socksProxyHost",
- "socksProxyPort"};
-
- private final AppSettings settings;
-
- public CommandFactoryImpl(AppSettings settings) {
- this.settings = settings;
- }
-
- @Override
- public EsCommand createEsCommand(AppSettings settings) {
- File homeDir = this.settings.getProps().nonNullValueAsFile(ProcessProperties.PATH_HOME);
- File executable = new File(homeDir, getExecutable());
- if (!executable.exists()) {
- throw new IllegalStateException("Cannot find elasticsearch binary");
- }
-
- Map<String, String> settingsMap = new EsSettings(this.settings.getProps()).build();
-
- File logDir = new File(settingsMap.get("path.logs"));
- File confDir = new File(settingsMap.get("path.conf"));
- EsCommand res = new EsCommand(ProcessId.ELASTICSEARCH)
- .setWorkDir(executable.getParentFile().getParentFile())
- .setExecutable(executable)
- .setConfDir(confDir)
- .setLog4j2Properties(new EsLogging().createProperties(settings.getProps(), logDir))
- .setArguments(this.settings.getProps().rawProperties())
- .setClusterName(settingsMap.get("cluster.name"))
- .setHost(settingsMap.get("network.host"))
- .setPort(Integer.valueOf(settingsMap.get("transport.tcp.port")))
- .addJvmOption(settings.getProps().nonNullValue(ProcessProperties.SEARCH_JAVA_OPTS))
- .addJvmOption(settings.getProps().nonNullValue(ProcessProperties.SEARCH_JAVA_ADDITIONAL_OPTS))
- .setEnvVariable("JAVA_HOME", System.getProperties().getProperty("java.home"));
-
- settingsMap.forEach((key, value) -> res.addEsOption("-E" + key + "=" + value));
-
- return res;
- }
-
- private static String getExecutable() {
- if (System.getProperty("os.name").startsWith("Windows")) {
- return "elasticsearch/bin/elasticsearch.bat";
- }
- return "elasticsearch/bin/elasticsearch";
- }
-
- @Override
- public JavaCommand createWebCommand(boolean leader) {
- File homeDir = settings.getProps().nonNullValueAsFile(ProcessProperties.PATH_HOME);
- JavaCommand command = newJavaCommand(ProcessId.WEB_SERVER, homeDir)
- .addJavaOptions(ProcessProperties.WEB_ENFORCED_JVM_ARGS)
- .addJavaOptions(settings.getProps().nonNullValue(ProcessProperties.WEB_JAVA_OPTS))
- .addJavaOptions(settings.getProps().nonNullValue(ProcessProperties.WEB_JAVA_ADDITIONAL_OPTS))
- // required for logback tomcat valve
- .setEnvVariable(ProcessProperties.PATH_LOGS, settings.getProps().nonNullValue(ProcessProperties.PATH_LOGS))
- .setArgument("sonar.cluster.web.startupLeader", Boolean.toString(leader))
- .setClassName("org.sonar.server.app.WebServer")
- .addClasspath("./lib/common/*")
- .addClasspath("./lib/server/*");
- String driverPath = settings.getProps().value(ProcessProperties.JDBC_DRIVER_PATH);
- if (driverPath != null) {
- command.addClasspath(driverPath);
- }
- return command;
- }
-
- @Override
- public JavaCommand createCeCommand() {
- File homeDir = settings.getProps().nonNullValueAsFile(ProcessProperties.PATH_HOME);
- JavaCommand command = newJavaCommand(ProcessId.COMPUTE_ENGINE, homeDir)
- .addJavaOptions(ProcessProperties.CE_ENFORCED_JVM_ARGS)
- .addJavaOptions(settings.getProps().nonNullValue(ProcessProperties.CE_JAVA_OPTS))
- .addJavaOptions(settings.getProps().nonNullValue(ProcessProperties.CE_JAVA_ADDITIONAL_OPTS))
- .setClassName("org.sonar.ce.app.CeServer")
- .addClasspath("./lib/common/*")
- .addClasspath("./lib/server/*")
- .addClasspath("./lib/ce/*");
- String driverPath = settings.getProps().value(ProcessProperties.JDBC_DRIVER_PATH);
- if (driverPath != null) {
- command.addClasspath(driverPath);
- }
- return command;
- }
-
- private JavaCommand newJavaCommand(ProcessId id, File homeDir) {
- JavaCommand command = new JavaCommand(id)
- .setWorkDir(homeDir)
- .setArguments(settings.getProps().rawProperties());
-
- for (String key : PROXY_PROPERTY_KEYS) {
- settings.getValue(key).ifPresent(val -> command.addJavaOption("-D" + key + "=" + val));
- }
-
- // defaults of HTTPS are the same than HTTP defaults
- setSystemPropertyToDefaultIfNotSet(command, HTTPS_PROXY_HOST, HTTP_PROXY_HOST);
- setSystemPropertyToDefaultIfNotSet(command, HTTPS_PROXY_PORT, HTTP_PROXY_PORT);
- return command;
- }
-
- private void setSystemPropertyToDefaultIfNotSet(JavaCommand command,
- String httpsProperty, String httpProperty) {
- Optional<String> httpValue = settings.getValue(httpProperty);
- Optional<String> httpsValue = settings.getValue(httpsProperty);
- if (!httpsValue.isPresent() && httpValue.isPresent()) {
- command.addJavaOption("-D" + httpsProperty + "=" + httpValue.get());
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.application.process;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Properties;
-import org.sonar.process.ProcessId;
-
-public class EsCommand extends AbstractCommand<EsCommand> {
- private File executable;
- private File confDir;
- private String clusterName;
- private String host;
- private int port;
- private Properties log4j2Properties;
- private List<String> esOptions = new ArrayList<>();
- private List<String> jvmOptions = new ArrayList<>();
-
- public EsCommand(ProcessId id) {
- super(id);
- }
-
- public File getExecutable() {
- return executable;
- }
-
- public EsCommand setExecutable(File executable) {
- this.executable = executable;
- return this;
- }
-
- public File getConfDir() {
- return confDir;
- }
-
- public EsCommand setConfDir(File confDir) {
- this.confDir = confDir;
- return this;
- }
-
- public String getClusterName() {
- return clusterName;
- }
-
- public EsCommand setClusterName(String clusterName) {
- this.clusterName = clusterName;
- return this;
- }
-
- public String getHost() {
- return host;
- }
-
- public EsCommand setHost(String host) {
- this.host = host;
- return this;
- }
-
- public int getPort() {
- return port;
- }
-
- public EsCommand setPort(int port) {
- this.port = port;
- return this;
- }
-
- public Properties getLog4j2Properties() {
- return log4j2Properties;
- }
-
- public EsCommand setLog4j2Properties(Properties log4j2Properties) {
- this.log4j2Properties = log4j2Properties;
- return this;
- }
-
- public List<String> getEsOptions() {
- return esOptions;
- }
-
- public EsCommand addEsOption(String s) {
- if (!s.isEmpty()) {
- esOptions.add(s);
- }
- return this;
- }
-
- public List<String> getJvmOptions() {
- return jvmOptions;
- }
-
- public EsCommand addJvmOption(String s) {
- if (!s.isEmpty()) {
- jvmOptions.add(s);
- }
- return this;
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.application.process;
-
-import ch.qos.logback.classic.Level;
-import java.io.File;
-import java.util.Properties;
-import org.sonar.process.ProcessId;
-import org.sonar.process.Props;
-import org.sonar.process.logging.Log4JPropertiesBuilder;
-import org.sonar.process.logging.LogLevelConfig;
-import org.sonar.process.logging.RootLoggerConfig;
-
-import static org.sonar.process.logging.RootLoggerConfig.newRootLoggerConfigBuilder;
-
-public class EsLogging {
-
- public Properties createProperties(Props props, File logDir) {
- Log4JPropertiesBuilder log4JPropertiesBuilder = new Log4JPropertiesBuilder(props);
- RootLoggerConfig config = newRootLoggerConfigBuilder().setProcessId(ProcessId.ELASTICSEARCH).build();
- String logPattern = log4JPropertiesBuilder.buildLogPattern(config);
-
- log4JPropertiesBuilder.internalLogLevel(Level.ERROR);
- log4JPropertiesBuilder.configureGlobalFileLog(config, logDir, logPattern);
- log4JPropertiesBuilder.apply(
- LogLevelConfig.newBuilder(log4JPropertiesBuilder.getRootLoggerName())
- .rootLevelFor(ProcessId.ELASTICSEARCH)
- .build());
-
- return log4JPropertiesBuilder.get();
- }
-
-}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.process.ProcessId;
+import org.sonar.process.command.EsCommand;
import static java.util.Collections.singletonList;
import static java.util.Collections.unmodifiableList;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.application.process;
-
-import java.io.File;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.UUID;
-import org.apache.commons.lang.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.sonar.process.ProcessProperties;
-import org.sonar.process.Props;
-
-import static java.lang.String.valueOf;
-
-public class EsSettings {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(EsSettings.class);
- private static final String PROP_MARVEL_HOSTS = "sonar.search.marvelHosts";
- private static final String CLUSTER_SEARCH_NODE_NAME = "sonar.cluster.search.nodeName";
- private static final String STANDALONE_NODE_NAME = "sonarqube";
-
- private final Props props;
-
- private final boolean clusterEnabled;
- private final String clusterName;
- private final String nodeName;
-
- EsSettings(Props props) {
- this.props = props;
-
- this.clusterName = props.nonNullValue(ProcessProperties.CLUSTER_NAME);
- this.clusterEnabled = props.valueAsBoolean(ProcessProperties.CLUSTER_ENABLED);
- if (this.clusterEnabled) {
- this.nodeName = props.value(CLUSTER_SEARCH_NODE_NAME, "sonarqube-" + UUID.randomUUID().toString());
- } else {
- this.nodeName = STANDALONE_NODE_NAME;
- }
- }
-
- Map<String, String> build() {
- Map<String, String> builder = new HashMap<>();
- configureFileSystem(builder);
- configureNetwork(builder);
- configureCluster(builder);
- configureMarvel(builder);
- configureAction(builder);
- return builder;
- }
-
- private void configureFileSystem(Map<String, String> builder) {
- File homeDir = props.nonNullValueAsFile(ProcessProperties.PATH_HOME);
-
- builder.put("path.data", buildDataPath(homeDir).getAbsolutePath());
- builder.put("path.conf", buildConfDir().getAbsolutePath());
- builder.put("path.logs", buildLogPath(homeDir).getAbsolutePath());
- }
-
- private File buildDataPath(File homeDir) {
- String dataPath = props.value(ProcessProperties.PATH_DATA);
- if (StringUtils.isNotEmpty(dataPath)) {
- return new File(dataPath, "es");
- }
- return new File(homeDir, "data/es");
- }
-
- private File buildLogPath(File homeDir) {
- String logPath = props.value(ProcessProperties.PATH_LOGS);
- if (StringUtils.isNotEmpty(logPath)) {
- return new File(logPath);
- }
- return new File(homeDir, "log");
- }
-
- private File buildConfDir() {
- String tempPath = props.value(ProcessProperties.PATH_TEMP);
- return new File(new File(tempPath, "conf"), "es");
- }
-
- private void configureNetwork(Map<String, String> builder) {
- InetAddress host = readHost();
- int port = Integer.parseInt(props.nonNullValue(ProcessProperties.SEARCH_PORT));
- LOGGER.info("Elasticsearch listening on {}:{}", host, port);
-
- builder.put("transport.tcp.port", valueOf(port));
- builder.put("transport.host", valueOf(host.getHostAddress()));
- builder.put("network.host", valueOf(host.getHostAddress()));
-
- // Elasticsearch sets the default value of TCP reuse address to true only on non-MSWindows machines, but why ?
- builder.put("network.tcp.reuse_address", valueOf(true));
-
- int httpPort = props.valueAsInt(ProcessProperties.SEARCH_HTTP_PORT, -1);
- if (httpPort < 0) {
- // standard configuration
- builder.put("http.enabled", valueOf(false));
- } else {
- LOGGER.warn("Elasticsearch HTTP connector is enabled on port {}. MUST NOT BE USED FOR PRODUCTION", httpPort);
- // see https://github.com/lmenezes/elasticsearch-kopf/issues/195
- builder.put("http.cors.enabled", valueOf(true));
- builder.put("http.cors.allow-origin", "*");
- builder.put("http.enabled", valueOf(true));
- builder.put("http.host", host.getHostAddress());
- builder.put("http.port", valueOf(httpPort));
- }
- }
-
- private InetAddress readHost() {
- String hostProperty = props.nonNullValue(ProcessProperties.SEARCH_HOST);
- try {
- return InetAddress.getByName(hostProperty);
- } catch (UnknownHostException e) {
- throw new IllegalStateException("Can not resolve host [" + hostProperty + "]. Please check network settings and property " + ProcessProperties.SEARCH_HOST, e);
- }
- }
-
- private void configureCluster(Map<String, String> builder) {
- // Default value in a standalone mode, not overridable
-
- int minimumMasterNodes = 1;
- String initialStateTimeOut = "30s";
-
- if (clusterEnabled) {
- minimumMasterNodes = props.valueAsInt(ProcessProperties.SEARCH_MINIMUM_MASTER_NODES, 2);
- initialStateTimeOut = props.value(ProcessProperties.SEARCH_INITIAL_STATE_TIMEOUT, "120s");
-
- String hosts = props.value(ProcessProperties.CLUSTER_SEARCH_HOSTS, "");
- LOGGER.info("Elasticsearch cluster enabled. Connect to hosts [{}]", hosts);
- builder.put("discovery.zen.ping.unicast.hosts", hosts);
- }
-
- builder.put("discovery.zen.minimum_master_nodes", valueOf(minimumMasterNodes));
- builder.put("discovery.initial_state_timeout", initialStateTimeOut);
- builder.put("cluster.name", clusterName);
- builder.put("cluster.routing.allocation.awareness.attributes", "rack_id");
- builder.put("node.attr.rack_id", nodeName);
- builder.put("node.name", nodeName);
- builder.put("node.data", valueOf(true));
- builder.put("node.master", valueOf(true));
- }
-
- private void configureMarvel(Map<String, String> builder) {
- Set<String> marvels = new TreeSet<>();
- marvels.addAll(Arrays.asList(StringUtils.split(props.value(PROP_MARVEL_HOSTS, ""), ",")));
-
- // If we're collecting indexing data send them to the Marvel host(s)
- if (!marvels.isEmpty()) {
- String hosts = StringUtils.join(marvels, ",");
- LOGGER.info("Elasticsearch Marvel is enabled for %s", hosts);
- builder.put("marvel.agent.exporter.es.hosts", hosts);
- }
- }
-
- private static void configureAction(Map<String, String> builder) {
- builder.put("action.auto_create_index", String.valueOf(false));
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.application.process;
-
-import java.util.ArrayList;
-import java.util.List;
-import org.sonar.process.ProcessId;
-
-public class JavaCommand extends AbstractCommand<JavaCommand> {
- // entry point
- private String className;
- // for example -Xmx1G
- private final List<String> javaOptions = new ArrayList<>();
- // relative path to JAR files
- private final List<String> classpath = new ArrayList<>();
-
- public JavaCommand(ProcessId id) {
- super(id);
- }
-
- public List<String> getJavaOptions() {
- return javaOptions;
- }
-
- public JavaCommand addJavaOption(String s) {
- if (!s.isEmpty()) {
- javaOptions.add(s);
- }
- return this;
- }
-
- public JavaCommand addJavaOptions(String s) {
- for (String opt : s.split(" ")) {
- addJavaOption(opt);
- }
- return this;
- }
-
- public String getClassName() {
- return className;
- }
-
- public JavaCommand setClassName(String className) {
- this.className = className;
- return this;
- }
-
- public List<String> getClasspath() {
- return classpath;
- }
-
- public JavaCommand addClasspath(String s) {
- classpath.add(s);
- return this;
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder("JavaCommand{");
- sb.append("workDir=").append(getWorkDir());
- sb.append(", javaOptions=").append(javaOptions);
- sb.append(", className='").append(className).append('\'');
- sb.append(", classpath=").append(classpath);
- sb.append(", arguments=").append(getArguments());
- sb.append(", envVariables=").append(getEnvVariables());
- sb.append('}');
- return sb.toString();
- }
-}
*/
package org.sonar.application.process;
-import org.sonar.process.sharedmemoryfile.ProcessCommands;
import org.sonar.process.ProcessId;
+import org.sonar.process.sharedmemoryfile.ProcessCommands;
import static java.util.Objects.requireNonNull;
package org.sonar.application.process;
import java.io.Closeable;
+import org.sonar.process.command.EsCommand;
+import org.sonar.process.command.JavaCommand;
public interface ProcessLauncher extends Closeable {
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.sonar.process.command.AbstractCommand;
+import org.sonar.process.command.EsCommand;
+import org.sonar.process.command.JavaCommand;
import org.sonar.process.sharedmemoryfile.AllProcessesCommands;
import org.sonar.process.sharedmemoryfile.ProcessCommands;
import org.sonar.process.ProcessId;
import org.junit.rules.TestRule;
import org.junit.rules.Timeout;
import org.mockito.Mockito;
-import org.sonar.application.config.AppSettings;
import org.sonar.application.config.TestAppSettings;
-import org.sonar.application.process.AbstractCommand;
-import org.sonar.application.process.CommandFactory;
-import org.sonar.application.process.EsCommand;
-import org.sonar.application.process.JavaCommand;
import org.sonar.application.process.ProcessLauncher;
import org.sonar.application.process.ProcessMonitor;
import org.sonar.process.ProcessId;
import org.sonar.process.ProcessProperties;
+import org.sonar.process.command.AbstractCommand;
+import org.sonar.process.command.CommandFactory;
+import org.sonar.process.command.EsCommand;
+import org.sonar.process.command.JavaCommand;
import static java.util.Collections.synchronizedList;
import static org.assertj.core.api.Assertions.assertThat;
private static class TestCommandFactory implements CommandFactory {
@Override
- public EsCommand createEsCommand(AppSettings settings) {
+ public EsCommand createEsCommand() {
return ES_COMMAND;
}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.application.process;
-
-import java.io.File;
-import java.util.Properties;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.process.ProcessId;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class AbstractCommandTest {
-
- @Rule
- public TemporaryFolder temp = new TemporaryFolder();
-
- @Test
- public void test_command_with_complete_information() throws Exception {
- AbstractCommand command = new AbstractCommand(ProcessId.ELASTICSEARCH) {
-
- };
-
- command.setArgument("first_arg", "val1");
- Properties args = new Properties();
- args.setProperty("second_arg", "val2");
- command.setArguments(args);
-
- command.setEnvVariable("JAVA_COMMAND_TEST", "1000");
- File workDir = temp.newFolder();
- command.setWorkDir(workDir);
-
- assertThat(command.toString()).isNotNull();
- assertThat(command.getWorkDir()).isSameAs(workDir);
-
- // copy current env variables
- assertThat(command.getEnvVariables().get("JAVA_COMMAND_TEST")).isEqualTo("1000");
- assertThat(command.getEnvVariables().size()).isEqualTo(System.getenv().size() + 1);
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.application.process;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.HashSet;
-import java.util.Properties;
-import java.util.Set;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.process.Props;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class EsLoggingTest {
- @Rule
- public TemporaryFolder temporaryFolder = new TemporaryFolder();
-
- private EsLogging underTest = new EsLogging();
-
- @Test
- public void createProperties_with_empty_props() throws IOException {
- File logDir = temporaryFolder.newFolder();
- Properties properties = underTest.createProperties(newProps(), logDir);
-
- verifyProperties(properties,
- "status", "ERROR",
- "appender.file_es.type", "RollingFile",
- "appender.file_es.name", "file_es",
- "appender.file_es.filePattern", new File(logDir, "es.%d{yyyy-MM-dd}.log").getAbsolutePath(),
- "appender.file_es.fileName", new File(logDir, "es.log").getAbsolutePath(),
- "appender.file_es.layout.type", "PatternLayout",
- "appender.file_es.layout.pattern", "%d{yyyy.MM.dd HH:mm:ss} %-5level es[][%logger{1.}] %msg%n",
- "appender.file_es.policies.type", "Policies",
- "appender.file_es.policies.time.type", "TimeBasedTriggeringPolicy",
- "appender.file_es.policies.time.interval", "1",
- "appender.file_es.policies.time.modulate", "true",
- "appender.file_es.strategy.type", "DefaultRolloverStrategy",
- "appender.file_es.strategy.fileIndex", "nomax",
- "appender.file_es.strategy.action.type", "Delete",
- "appender.file_es.strategy.action.basepath", logDir.getAbsolutePath(),
- "appender.file_es.strategy.action.maxDepth", "1",
- "appender.file_es.strategy.action.condition.type", "IfFileName",
- "appender.file_es.strategy.action.condition.glob", "es*",
- "appender.file_es.strategy.action.condition.nested_condition.type", "IfAccumulatedFileCount",
- "appender.file_es.strategy.action.condition.nested_condition.exceeds", "7",
- "rootLogger.level", "INFO",
- "rootLogger.appenderRef.file_es.ref", "file_es");
- }
-
- @Test
- public void createProperties_sets_root_logger_to_INFO_if_no_property_is_set() throws IOException {
- File logDir = temporaryFolder.newFolder();
- Properties properties = underTest.createProperties(newProps(), logDir);
-
- assertThat(properties.getProperty("rootLogger.level")).isEqualTo("INFO");
- }
-
- @Test
- public void createProperties_sets_root_logger_to_global_property_if_set() throws IOException {
- File logDir = temporaryFolder.newFolder();
- Properties properties = underTest.createProperties(newProps("sonar.log.level", "TRACE"), logDir);
-
- assertThat(properties.getProperty("rootLogger.level")).isEqualTo("TRACE");
- }
-
- @Test
- public void createProperties_sets_root_logger_to_process_property_if_set() throws IOException {
- File logDir = temporaryFolder.newFolder();
- Properties properties = underTest.createProperties(newProps("sonar.log.level.es", "DEBUG"), logDir);
-
- assertThat(properties.getProperty("rootLogger.level")).isEqualTo("DEBUG");
- }
-
- @Test
- public void createProperties_sets_root_logger_to_process_property_over_global_property_if_both_set() throws IOException {
- File logDir = temporaryFolder.newFolder();
- Properties properties = underTest.createProperties(
- newProps(
- "sonar.log.level", "DEBUG",
- "sonar.log.level.es", "TRACE"),
- logDir);
-
- assertThat(properties.getProperty("rootLogger.level")).isEqualTo("TRACE");
- }
-
- private static Props newProps(String... propertyKeysAndValues) {
- assertThat(propertyKeysAndValues.length % 2).describedAs("Number of parameters must be even").isEqualTo(0);
- Properties properties = new Properties();
- for (int i = 0; i < propertyKeysAndValues.length; i++) {
- properties.put(propertyKeysAndValues[i++], propertyKeysAndValues[i]);
- }
- return new Props(properties);
- }
-
- private void verifyProperties(Properties properties, String... expectedPropertyKeysAndValuesOrdered) {
- if (expectedPropertyKeysAndValuesOrdered.length == 0) {
- assertThat(properties.size()).isEqualTo(0);
- } else {
- assertThat(expectedPropertyKeysAndValuesOrdered.length % 2).describedAs("Number of parameters must be even").isEqualTo(0);
- Set<String> keys = new HashSet<>(expectedPropertyKeysAndValuesOrdered.length / 2 + 1);
- keys.add("status");
- for (int i = 0; i < expectedPropertyKeysAndValuesOrdered.length; i++) {
- String key = expectedPropertyKeysAndValuesOrdered[i++];
- String value = expectedPropertyKeysAndValuesOrdered[i];
- assertThat(properties.get(key)).describedAs("Unexpected value for property " + key).isEqualTo(value);
- keys.add(key);
- }
- assertThat(properties.keySet()).containsOnly(keys.toArray());
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.application.process;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Map;
-import java.util.Properties;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.process.ProcessProperties;
-import org.sonar.process.Props;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class EsSettingsTest {
-
- private static final boolean CLUSTER_ENABLED = true;
- private static final boolean CLUSTER_DISABLED = false;
-
- @Rule
- public TemporaryFolder temp = new TemporaryFolder();
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- @Test
- public void test_default_settings() throws Exception {
- File homeDir = temp.newFolder();
- Props props = new Props(new Properties());
- props.set(ProcessProperties.SEARCH_PORT, "1234");
- props.set(ProcessProperties.SEARCH_HOST, "127.0.0.1");
- props.set(ProcessProperties.PATH_HOME, homeDir.getAbsolutePath());
- props.set(ProcessProperties.CLUSTER_NAME, "sonarqube");
-
- EsSettings esSettings = new EsSettings(props);
-
- Map<String, String> generated = esSettings.build();
- assertThat(generated.get("transport.tcp.port")).isEqualTo("1234");
- assertThat(generated.get("transport.host")).isEqualTo("127.0.0.1");
-
- // no cluster, but cluster and node names are set though
- assertThat(generated.get("cluster.name")).isEqualTo("sonarqube");
- assertThat(generated.get("node.name")).isEqualTo("sonarqube");
-
- assertThat(generated.get("path.data")).isNotNull();
- assertThat(generated.get("path.logs")).isNotNull();
- assertThat(generated.get("path.home")).isNull();
- assertThat(generated.get("path.conf")).isNotNull();
-
- // http is disabled for security reasons
- assertThat(generated.get("http.enabled")).isEqualTo("false");
-
- assertThat(generated.get("discovery.zen.ping.unicast.hosts")).isNull();
- assertThat(generated.get("discovery.zen.minimum_master_nodes")).isEqualTo("1");
- assertThat(generated.get("discovery.initial_state_timeout")).isEqualTo("30s");
-
- assertThat(generated.get("action.auto_create_index")).isEqualTo("false");
- }
-
- @Test
- public void override_dirs() throws Exception {
- File dataDir = temp.newFolder();
- File logDir = temp.newFolder();
- File tempDir = temp.newFolder();
- Props props = minProps(CLUSTER_DISABLED);
- props.set(ProcessProperties.PATH_DATA, dataDir.getAbsolutePath());
- props.set(ProcessProperties.PATH_LOGS, logDir.getAbsolutePath());
- props.set(ProcessProperties.PATH_TEMP, tempDir.getAbsolutePath());
-
- Map<String, String> settings = new EsSettings(props).build();
-
- assertThat(settings.get("path.data")).isEqualTo(new File(dataDir, "es").getAbsolutePath());
- assertThat(settings.get("path.logs")).isEqualTo(logDir.getAbsolutePath());
- assertThat(settings.get("path.home")).isNull();
- assertThat(settings.get("path.conf")).isEqualTo(new File(tempDir, "conf/es").getAbsolutePath());
- }
-
- @Test
- public void set_discovery_settings_if_cluster_is_enabled() throws Exception {
- Props props = minProps(CLUSTER_ENABLED);
- props.set(ProcessProperties.CLUSTER_SEARCH_HOSTS, "1.2.3.4:9000,1.2.3.5:8080");
- Map<String, String> settings = new EsSettings(props).build();
-
- assertThat(settings.get("discovery.zen.ping.unicast.hosts")).isEqualTo("1.2.3.4:9000,1.2.3.5:8080");
- assertThat(settings.get("discovery.zen.minimum_master_nodes")).isEqualTo("2");
- assertThat(settings.get("discovery.initial_state_timeout")).isEqualTo("120s");
- }
-
- @Test
- public void incorrect_values_of_minimum_master_nodes() throws Exception {
- Props props = minProps(CLUSTER_ENABLED);
- props.set(ProcessProperties.SEARCH_MINIMUM_MASTER_NODES, "ꝱꝲꝳପ");
-
- EsSettings underTest = new EsSettings(props);
-
- expectedException.expect(IllegalStateException.class);
- expectedException.expectMessage("Value of property sonar.search.minimumMasterNodes is not an integer:");
- underTest.build();
- }
-
- @Test
- public void cluster_is_enabled_with_defined_minimum_master_nodes() throws Exception {
- Props props = minProps(CLUSTER_ENABLED);
- props.set(ProcessProperties.SEARCH_MINIMUM_MASTER_NODES, "5");
- Map<String, String> settings = new EsSettings(props).build();
-
- assertThat(settings.get("discovery.zen.minimum_master_nodes")).isEqualTo("5");
- }
-
- @Test
- public void cluster_is_enabled_with_defined_initialTimeout() throws Exception {
- Props props = minProps(CLUSTER_ENABLED);
- props.set(ProcessProperties.SEARCH_INITIAL_STATE_TIMEOUT, "10s");
- Map<String, String> settings = new EsSettings(props).build();
-
- assertThat(settings.get("discovery.initial_state_timeout")).isEqualTo("10s");
- }
-
- @Test
- public void in_standalone_initialTimeout_is_not_overridable() throws Exception {
- Props props = minProps(CLUSTER_DISABLED);
- props.set(ProcessProperties.SEARCH_INITIAL_STATE_TIMEOUT, "10s");
- Map<String, String> settings = new EsSettings(props).build();
-
- assertThat(settings.get("discovery.initial_state_timeout")).isEqualTo("30s");
- }
-
- @Test
- public void in_standalone_minimumMasterNodes_is_not_overridable() throws Exception {
- Props props = minProps(CLUSTER_DISABLED);
- props.set(ProcessProperties.SEARCH_MINIMUM_MASTER_NODES, "5");
- Map<String, String> settings = new EsSettings(props).build();
-
- assertThat(settings.get("discovery.zen.minimum_master_nodes")).isEqualTo("1");
- }
-
-
-
- @Test
- public void enable_marvel() throws Exception {
- Props props = minProps(CLUSTER_DISABLED);
- props.set("sonar.search.marvelHosts", "127.0.0.2,127.0.0.3");
- Map<String, String> settings = new EsSettings(props).build();
-
- assertThat(settings.get("marvel.agent.exporter.es.hosts")).isEqualTo("127.0.0.2,127.0.0.3");
- }
-
- @Test
- public void enable_http_connector() throws Exception {
- Props props = minProps(CLUSTER_DISABLED);
- props.set(ProcessProperties.SEARCH_HTTP_PORT, "9010");
- Map<String, String> settings = new EsSettings(props).build();
-
- assertThat(settings.get("http.port")).isEqualTo("9010");
- assertThat(settings.get("http.host")).isEqualTo("127.0.0.1");
- assertThat(settings.get("http.enabled")).isEqualTo("true");
- }
-
- @Test
- public void enable_http_connector_different_host() throws Exception {
- Props props = minProps(CLUSTER_DISABLED);
- props.set(ProcessProperties.SEARCH_HTTP_PORT, "9010");
- props.set(ProcessProperties.SEARCH_HOST, "127.0.0.2");
- Map<String, String> settings = new EsSettings(props).build();
-
- assertThat(settings.get("http.port")).isEqualTo("9010");
- assertThat(settings.get("http.host")).isEqualTo("127.0.0.2");
- assertThat(settings.get("http.enabled")).isEqualTo("true");
- }
-
- private Props minProps(boolean cluster) throws IOException {
- File homeDir = temp.newFolder();
- Props props = new Props(new Properties());
- ProcessProperties.completeDefaults(props);
- props.set(ProcessProperties.PATH_HOME, homeDir.getAbsolutePath());
- props.set(ProcessProperties.CLUSTER_ENABLED, Boolean.toString(cluster));
- return props;
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.application.process;
-
-import java.io.File;
-import java.util.Properties;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.process.ProcessId;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class JavaCommandTest {
-
- @Rule
- public TemporaryFolder temp = new TemporaryFolder();
-
- @Test
- public void test_command_with_complete_information() throws Exception {
- JavaCommand command = new JavaCommand(ProcessId.ELASTICSEARCH);
-
- command.setArgument("first_arg", "val1");
- Properties args = new Properties();
- args.setProperty("second_arg", "val2");
- command.setArguments(args);
-
- command.setClassName("org.sonar.ElasticSearch");
- command.setEnvVariable("JAVA_COMMAND_TEST", "1000");
- File workDir = temp.newFolder();
- command.setWorkDir(workDir);
- command.addClasspath("lib/*.jar");
- command.addClasspath("conf/*.xml");
- command.addJavaOption("-Xmx128m");
-
- assertThat(command.toString()).isNotNull();
- assertThat(command.getClasspath()).containsOnly("lib/*.jar", "conf/*.xml");
- assertThat(command.getJavaOptions()).containsOnly("-Xmx128m");
- assertThat(command.getWorkDir()).isSameAs(workDir);
- assertThat(command.getClassName()).isEqualTo("org.sonar.ElasticSearch");
-
- // copy current env variables
- assertThat(command.getEnvVariables().get("JAVA_COMMAND_TEST")).isEqualTo("1000");
- assertThat(command.getEnvVariables().size()).isEqualTo(System.getenv().size() + 1);
- }
-
- @Test
- public void addJavaOptions_adds_jvm_options() {
- JavaCommand command = new JavaCommand(ProcessId.ELASTICSEARCH);
- assertThat(command.getJavaOptions()).isEmpty();
-
- command.addJavaOptions("");
- assertThat(command.getJavaOptions()).isEmpty();
-
- command.addJavaOptions("-Xmx512m -Xms256m -Dfoo");
- assertThat(command.getJavaOptions()).containsOnly("-Xmx512m", "-Xms256m", "-Dfoo");
- }
-}
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
+import org.sonar.process.command.JavaCommand;
import org.sonar.process.sharedmemoryfile.AllProcessesCommands;
import org.sonar.process.ProcessId;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.command;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Properties;
+import javax.annotation.Nullable;
+import org.sonar.process.ProcessId;
+
+public abstract class AbstractCommand<T extends AbstractCommand> {
+ // unique key among the group of commands to launch
+ private final ProcessId id;
+ // program arguments
+ private final Map<String, String> arguments = new LinkedHashMap<>();
+ private final Map<String, String> envVariables = new HashMap<>(System.getenv());
+ private File workDir;
+
+ protected AbstractCommand(ProcessId id) {
+ this.id = id;
+ }
+
+ public ProcessId getProcessId() {
+ return id;
+ }
+
+ public File getWorkDir() {
+ return workDir;
+ }
+
+ public T setWorkDir(File workDir) {
+ this.workDir = workDir;
+ return castThis();
+ }
+
+ @SuppressWarnings("unchecked")
+ private T castThis() {
+ return (T) this;
+ }
+
+ public Map<String, String> getArguments() {
+ return arguments;
+ }
+
+ public T setArgument(String key, @Nullable String value) {
+ if (value == null) {
+ arguments.remove(key);
+ } else {
+ arguments.put(key, value);
+ }
+ return castThis();
+ }
+
+ public T setArguments(Properties args) {
+ for (Map.Entry<Object, Object> entry : args.entrySet()) {
+ setArgument(entry.getKey().toString(), entry.getValue() != null ? entry.getValue().toString() : null);
+ }
+ return castThis();
+ }
+
+ public Map<String, String> getEnvVariables() {
+ return envVariables;
+ }
+
+ public T setEnvVariable(String key, @Nullable String value) {
+ if (value == null) {
+ envVariables.remove(key);
+ } else {
+ envVariables.put(key, value);
+ }
+ return castThis();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.command;
+
+public interface CommandFactory {
+
+ EsCommand createEsCommand();
+
+ JavaCommand createWebCommand(boolean leader);
+
+ JavaCommand createCeCommand();
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.command;
+
+import java.io.File;
+import java.util.Map;
+import java.util.Optional;
+import org.sonar.process.ProcessId;
+import org.sonar.process.ProcessProperties;
+import org.sonar.process.Props;
+import org.sonar.process.es.EsLogging;
+import org.sonar.process.es.EsSettings;
+
+import static org.sonar.process.ProcessProperties.HTTPS_PROXY_HOST;
+import static org.sonar.process.ProcessProperties.HTTPS_PROXY_PORT;
+import static org.sonar.process.ProcessProperties.HTTP_PROXY_HOST;
+import static org.sonar.process.ProcessProperties.HTTP_PROXY_PORT;
+
+public class CommandFactoryImpl implements CommandFactory {
+ /**
+ * Properties about proxy that must be set as system properties
+ */
+ private static final String[] PROXY_PROPERTY_KEYS = new String[] {
+ HTTP_PROXY_HOST,
+ HTTP_PROXY_PORT,
+ "http.nonProxyHosts",
+ HTTPS_PROXY_HOST,
+ HTTPS_PROXY_PORT,
+ "http.auth.ntlm.domain",
+ "socksProxyHost",
+ "socksProxyPort"};
+
+ private final Props props;
+
+ public CommandFactoryImpl(Props props) {
+ this.props = props;
+ }
+
+ @Override
+ public EsCommand createEsCommand() {
+ File homeDir = props.nonNullValueAsFile(ProcessProperties.PATH_HOME);
+ File executable = new File(homeDir, getExecutable());
+ if (!executable.exists()) {
+ throw new IllegalStateException("Cannot find elasticsearch binary");
+ }
+
+ Map<String, String> settingsMap = new EsSettings(props).build();
+
+ File logDir = new File(settingsMap.get("path.logs"));
+ File confDir = new File(settingsMap.get("path.conf"));
+ EsCommand res = new EsCommand(ProcessId.ELASTICSEARCH)
+ .setWorkDir(executable.getParentFile().getParentFile())
+ .setExecutable(executable)
+ .setConfDir(confDir)
+ .setLog4j2Properties(new EsLogging().createProperties(props, logDir))
+ .setArguments(props.rawProperties())
+ .setClusterName(settingsMap.get("cluster.name"))
+ .setHost(settingsMap.get("network.host"))
+ .setPort(Integer.valueOf(settingsMap.get("transport.tcp.port")))
+ .addJvmOption(props.nonNullValue(ProcessProperties.SEARCH_JAVA_OPTS))
+ .addJvmOption(props.nonNullValue(ProcessProperties.SEARCH_JAVA_ADDITIONAL_OPTS))
+ .setEnvVariable("JAVA_HOME", System.getProperties().getProperty("java.home"));
+
+ settingsMap.forEach((key, value) -> res.addEsOption("-E" + key + "=" + value));
+
+ return res;
+ }
+
+ private static String getExecutable() {
+ if (System.getProperty("os.name").startsWith("Windows")) {
+ return "elasticsearch/bin/elasticsearch.bat";
+ }
+ return "elasticsearch/bin/elasticsearch";
+ }
+
+ @Override
+ public JavaCommand createWebCommand(boolean leader) {
+ File homeDir = props.nonNullValueAsFile(ProcessProperties.PATH_HOME);
+ JavaCommand command = newJavaCommand(ProcessId.WEB_SERVER, homeDir)
+ .addJavaOptions(ProcessProperties.WEB_ENFORCED_JVM_ARGS)
+ .addJavaOptions(props.nonNullValue(ProcessProperties.WEB_JAVA_OPTS))
+ .addJavaOptions(props.nonNullValue(ProcessProperties.WEB_JAVA_ADDITIONAL_OPTS))
+ // required for logback tomcat valve
+ .setEnvVariable(ProcessProperties.PATH_LOGS, props.nonNullValue(ProcessProperties.PATH_LOGS))
+ .setArgument("sonar.cluster.web.startupLeader", Boolean.toString(leader))
+ .setClassName("org.sonar.server.app.WebServer")
+ .addClasspath("./lib/common/*")
+ .addClasspath("./lib/server/*");
+ String driverPath = props.value(ProcessProperties.JDBC_DRIVER_PATH);
+ if (driverPath != null) {
+ command.addClasspath(driverPath);
+ }
+ return command;
+ }
+
+ @Override
+ public JavaCommand createCeCommand() {
+ File homeDir = props.nonNullValueAsFile(ProcessProperties.PATH_HOME);
+ JavaCommand command = newJavaCommand(ProcessId.COMPUTE_ENGINE, homeDir)
+ .addJavaOptions(ProcessProperties.CE_ENFORCED_JVM_ARGS)
+ .addJavaOptions(props.nonNullValue(ProcessProperties.CE_JAVA_OPTS))
+ .addJavaOptions(props.nonNullValue(ProcessProperties.CE_JAVA_ADDITIONAL_OPTS))
+ .setClassName("org.sonar.ce.app.CeServer")
+ .addClasspath("./lib/common/*")
+ .addClasspath("./lib/server/*")
+ .addClasspath("./lib/ce/*");
+ String driverPath = props.value(ProcessProperties.JDBC_DRIVER_PATH);
+ if (driverPath != null) {
+ command.addClasspath(driverPath);
+ }
+ return command;
+ }
+
+ private JavaCommand newJavaCommand(ProcessId id, File homeDir) {
+ JavaCommand command = new JavaCommand(id)
+ .setWorkDir(homeDir)
+ .setArguments(props.rawProperties());
+
+ for (String key : PROXY_PROPERTY_KEYS) {
+ getPropsValue(key).ifPresent(val -> command.addJavaOption("-D" + key + "=" + val));
+ }
+
+ // defaults of HTTPS are the same than HTTP defaults
+ setSystemPropertyToDefaultIfNotSet(command, HTTPS_PROXY_HOST, HTTP_PROXY_HOST);
+ setSystemPropertyToDefaultIfNotSet(command, HTTPS_PROXY_PORT, HTTP_PROXY_PORT);
+ return command;
+ }
+
+ private void setSystemPropertyToDefaultIfNotSet(JavaCommand command,
+ String httpsProperty, String httpProperty) {
+ Optional<String> httpValue = getPropsValue(httpProperty);
+ Optional<String> httpsValue = getPropsValue(httpsProperty);
+ if (!httpsValue.isPresent() && httpValue.isPresent()) {
+ command.addJavaOption("-D" + httpsProperty + "=" + httpValue.get());
+ }
+ }
+
+ private Optional<String> getPropsValue(String key) {
+ return Optional.ofNullable(props.value(key));
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.command;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import org.sonar.process.ProcessId;
+
+public class EsCommand extends AbstractCommand<EsCommand> {
+ private File executable;
+ private File confDir;
+ private String clusterName;
+ private String host;
+ private int port;
+ private Properties log4j2Properties;
+ private List<String> esOptions = new ArrayList<>();
+ private List<String> jvmOptions = new ArrayList<>();
+
+ public EsCommand(ProcessId id) {
+ super(id);
+ }
+
+ public File getExecutable() {
+ return executable;
+ }
+
+ public EsCommand setExecutable(File executable) {
+ this.executable = executable;
+ return this;
+ }
+
+ public File getConfDir() {
+ return confDir;
+ }
+
+ public EsCommand setConfDir(File confDir) {
+ this.confDir = confDir;
+ return this;
+ }
+
+ public String getClusterName() {
+ return clusterName;
+ }
+
+ public EsCommand setClusterName(String clusterName) {
+ this.clusterName = clusterName;
+ return this;
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public EsCommand setHost(String host) {
+ this.host = host;
+ return this;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public EsCommand setPort(int port) {
+ this.port = port;
+ return this;
+ }
+
+ public Properties getLog4j2Properties() {
+ return log4j2Properties;
+ }
+
+ public EsCommand setLog4j2Properties(Properties log4j2Properties) {
+ this.log4j2Properties = log4j2Properties;
+ return this;
+ }
+
+ public List<String> getEsOptions() {
+ return esOptions;
+ }
+
+ public EsCommand addEsOption(String s) {
+ if (!s.isEmpty()) {
+ esOptions.add(s);
+ }
+ return this;
+ }
+
+ public List<String> getJvmOptions() {
+ return jvmOptions;
+ }
+
+ public EsCommand addJvmOption(String s) {
+ if (!s.isEmpty()) {
+ jvmOptions.add(s);
+ }
+ return this;
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.command;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.sonar.process.ProcessId;
+
+public class JavaCommand extends AbstractCommand<JavaCommand> {
+ // entry point
+ private String className;
+ // for example -Xmx1G
+ private final List<String> javaOptions = new ArrayList<>();
+ // relative path to JAR files
+ private final List<String> classpath = new ArrayList<>();
+
+ public JavaCommand(ProcessId id) {
+ super(id);
+ }
+
+ public List<String> getJavaOptions() {
+ return javaOptions;
+ }
+
+ public JavaCommand addJavaOption(String s) {
+ if (!s.isEmpty()) {
+ javaOptions.add(s);
+ }
+ return this;
+ }
+
+ public JavaCommand addJavaOptions(String s) {
+ for (String opt : s.split(" ")) {
+ addJavaOption(opt);
+ }
+ return this;
+ }
+
+ public String getClassName() {
+ return className;
+ }
+
+ public JavaCommand setClassName(String className) {
+ this.className = className;
+ return this;
+ }
+
+ public List<String> getClasspath() {
+ return classpath;
+ }
+
+ public JavaCommand addClasspath(String s) {
+ classpath.add(s);
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("JavaCommand{");
+ sb.append("workDir=").append(getWorkDir());
+ sb.append(", javaOptions=").append(javaOptions);
+ sb.append(", className='").append(className).append('\'');
+ sb.append(", classpath=").append(classpath);
+ sb.append(", arguments=").append(getArguments());
+ sb.append(", envVariables=").append(getEnvVariables());
+ sb.append('}');
+ return sb.toString();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.process.command;
+
+import javax.annotation.ParametersAreNonnullByDefault;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.es;
+
+import ch.qos.logback.classic.Level;
+import java.io.File;
+import java.util.Properties;
+import org.sonar.process.ProcessId;
+import org.sonar.process.Props;
+import org.sonar.process.logging.Log4JPropertiesBuilder;
+import org.sonar.process.logging.LogLevelConfig;
+import org.sonar.process.logging.RootLoggerConfig;
+
+import static org.sonar.process.logging.RootLoggerConfig.newRootLoggerConfigBuilder;
+
+public class EsLogging {
+
+ public Properties createProperties(Props props, File logDir) {
+ Log4JPropertiesBuilder log4JPropertiesBuilder = new Log4JPropertiesBuilder(props);
+ RootLoggerConfig config = newRootLoggerConfigBuilder().setProcessId(ProcessId.ELASTICSEARCH).build();
+ String logPattern = log4JPropertiesBuilder.buildLogPattern(config);
+
+ log4JPropertiesBuilder.internalLogLevel(Level.ERROR);
+ log4JPropertiesBuilder.configureGlobalFileLog(config, logDir, logPattern);
+ log4JPropertiesBuilder.apply(
+ LogLevelConfig.newBuilder(log4JPropertiesBuilder.getRootLoggerName())
+ .rootLevelFor(ProcessId.ELASTICSEARCH)
+ .build());
+
+ return log4JPropertiesBuilder.get();
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.es;
+
+import java.io.File;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.UUID;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.process.ProcessProperties;
+import org.sonar.process.Props;
+
+import static java.lang.String.valueOf;
+
+public class EsSettings {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(EsSettings.class);
+ private static final String PROP_MARVEL_HOSTS = "sonar.search.marvelHosts";
+ private static final String CLUSTER_SEARCH_NODE_NAME = "sonar.cluster.search.nodeName";
+ private static final String STANDALONE_NODE_NAME = "sonarqube";
+
+ private final Props props;
+
+ private final boolean clusterEnabled;
+ private final String clusterName;
+ private final String nodeName;
+
+ public EsSettings(Props props) {
+ this.props = props;
+
+ this.clusterName = props.nonNullValue(ProcessProperties.CLUSTER_NAME);
+ this.clusterEnabled = props.valueAsBoolean(ProcessProperties.CLUSTER_ENABLED);
+ if (this.clusterEnabled) {
+ this.nodeName = props.value(CLUSTER_SEARCH_NODE_NAME, "sonarqube-" + UUID.randomUUID().toString());
+ } else {
+ this.nodeName = STANDALONE_NODE_NAME;
+ }
+ }
+
+ public Map<String, String> build() {
+ Map<String, String> builder = new HashMap<>();
+ configureFileSystem(builder);
+ configureNetwork(builder);
+ configureCluster(builder);
+ configureMarvel(builder);
+ configureAction(builder);
+ return builder;
+ }
+
+ private void configureFileSystem(Map<String, String> builder) {
+ File homeDir = props.nonNullValueAsFile(ProcessProperties.PATH_HOME);
+
+ builder.put("path.data", buildDataPath(homeDir).getAbsolutePath());
+ builder.put("path.conf", buildConfDir().getAbsolutePath());
+ builder.put("path.logs", buildLogPath(homeDir).getAbsolutePath());
+ }
+
+ private File buildDataPath(File homeDir) {
+ String dataPath = props.value(ProcessProperties.PATH_DATA);
+ if (StringUtils.isNotEmpty(dataPath)) {
+ return new File(dataPath, "es");
+ }
+ return new File(homeDir, "data/es");
+ }
+
+ private File buildLogPath(File homeDir) {
+ String logPath = props.value(ProcessProperties.PATH_LOGS);
+ if (StringUtils.isNotEmpty(logPath)) {
+ return new File(logPath);
+ }
+ return new File(homeDir, "log");
+ }
+
+ private File buildConfDir() {
+ String tempPath = props.value(ProcessProperties.PATH_TEMP);
+ return new File(new File(tempPath, "conf"), "es");
+ }
+
+ private void configureNetwork(Map<String, String> builder) {
+ InetAddress host = readHost();
+ int port = Integer.parseInt(props.nonNullValue(ProcessProperties.SEARCH_PORT));
+ LOGGER.info("Elasticsearch listening on {}:{}", host, port);
+
+ builder.put("transport.tcp.port", valueOf(port));
+ builder.put("transport.host", valueOf(host.getHostAddress()));
+ builder.put("network.host", valueOf(host.getHostAddress()));
+
+ // Elasticsearch sets the default value of TCP reuse address to true only on non-MSWindows machines, but why ?
+ builder.put("network.tcp.reuse_address", valueOf(true));
+
+ int httpPort = props.valueAsInt(ProcessProperties.SEARCH_HTTP_PORT, -1);
+ if (httpPort < 0) {
+ // standard configuration
+ builder.put("http.enabled", valueOf(false));
+ } else {
+ LOGGER.warn("Elasticsearch HTTP connector is enabled on port {}. MUST NOT BE USED FOR PRODUCTION", httpPort);
+ // see https://github.com/lmenezes/elasticsearch-kopf/issues/195
+ builder.put("http.cors.enabled", valueOf(true));
+ builder.put("http.cors.allow-origin", "*");
+ builder.put("http.enabled", valueOf(true));
+ builder.put("http.host", host.getHostAddress());
+ builder.put("http.port", valueOf(httpPort));
+ }
+ }
+
+ private InetAddress readHost() {
+ String hostProperty = props.nonNullValue(ProcessProperties.SEARCH_HOST);
+ try {
+ return InetAddress.getByName(hostProperty);
+ } catch (UnknownHostException e) {
+ throw new IllegalStateException("Can not resolve host [" + hostProperty + "]. Please check network settings and property " + ProcessProperties.SEARCH_HOST, e);
+ }
+ }
+
+ private void configureCluster(Map<String, String> builder) {
+ // Default value in a standalone mode, not overridable
+
+ int minimumMasterNodes = 1;
+ String initialStateTimeOut = "30s";
+
+ if (clusterEnabled) {
+ minimumMasterNodes = props.valueAsInt(ProcessProperties.SEARCH_MINIMUM_MASTER_NODES, 2);
+ initialStateTimeOut = props.value(ProcessProperties.SEARCH_INITIAL_STATE_TIMEOUT, "120s");
+
+ String hosts = props.value(ProcessProperties.CLUSTER_SEARCH_HOSTS, "");
+ LOGGER.info("Elasticsearch cluster enabled. Connect to hosts [{}]", hosts);
+ builder.put("discovery.zen.ping.unicast.hosts", hosts);
+ }
+
+ builder.put("discovery.zen.minimum_master_nodes", valueOf(minimumMasterNodes));
+ builder.put("discovery.initial_state_timeout", initialStateTimeOut);
+ builder.put("cluster.name", clusterName);
+ builder.put("cluster.routing.allocation.awareness.attributes", "rack_id");
+ builder.put("node.attr.rack_id", nodeName);
+ builder.put("node.name", nodeName);
+ builder.put("node.data", valueOf(true));
+ builder.put("node.master", valueOf(true));
+ }
+
+ private void configureMarvel(Map<String, String> builder) {
+ Set<String> marvels = new TreeSet<>();
+ marvels.addAll(Arrays.asList(StringUtils.split(props.value(PROP_MARVEL_HOSTS, ""), ",")));
+
+ // If we're collecting indexing data send them to the Marvel host(s)
+ if (!marvels.isEmpty()) {
+ String hosts = StringUtils.join(marvels, ",");
+ LOGGER.info("Elasticsearch Marvel is enabled for %s", hosts);
+ builder.put("marvel.agent.exporter.es.hosts", hosts);
+ }
+ }
+
+ private static void configureAction(Map<String, String> builder) {
+ builder.put("action.auto_create_index", String.valueOf(false));
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.process.es;
+
+import javax.annotation.ParametersAreNonnullByDefault;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.command;
+
+import java.io.File;
+import java.util.Properties;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.process.ProcessId;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class AbstractCommandTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Test
+ public void test_command_with_complete_information() throws Exception {
+ AbstractCommand command = new AbstractCommand(ProcessId.ELASTICSEARCH) {
+
+ };
+
+ command.setArgument("first_arg", "val1");
+ Properties args = new Properties();
+ args.setProperty("second_arg", "val2");
+ command.setArguments(args);
+
+ command.setEnvVariable("JAVA_COMMAND_TEST", "1000");
+ File workDir = temp.newFolder();
+ command.setWorkDir(workDir);
+
+ assertThat(command.toString()).isNotNull();
+ assertThat(command.getWorkDir()).isSameAs(workDir);
+
+ // copy current env variables
+ assertThat(command.getEnvVariables().get("JAVA_COMMAND_TEST")).isEqualTo("1000");
+ assertThat(command.getEnvVariables().size()).isEqualTo(System.getenv().size() + 1);
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.command;
+
+import java.io.File;
+import java.util.Properties;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.process.ProcessId;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class JavaCommandTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Test
+ public void test_command_with_complete_information() throws Exception {
+ JavaCommand command = new JavaCommand(ProcessId.ELASTICSEARCH);
+
+ command.setArgument("first_arg", "val1");
+ Properties args = new Properties();
+ args.setProperty("second_arg", "val2");
+ command.setArguments(args);
+
+ command.setClassName("org.sonar.ElasticSearch");
+ command.setEnvVariable("JAVA_COMMAND_TEST", "1000");
+ File workDir = temp.newFolder();
+ command.setWorkDir(workDir);
+ command.addClasspath("lib/*.jar");
+ command.addClasspath("conf/*.xml");
+ command.addJavaOption("-Xmx128m");
+
+ assertThat(command.toString()).isNotNull();
+ assertThat(command.getClasspath()).containsOnly("lib/*.jar", "conf/*.xml");
+ assertThat(command.getJavaOptions()).containsOnly("-Xmx128m");
+ assertThat(command.getWorkDir()).isSameAs(workDir);
+ assertThat(command.getClassName()).isEqualTo("org.sonar.ElasticSearch");
+
+ // copy current env variables
+ assertThat(command.getEnvVariables().get("JAVA_COMMAND_TEST")).isEqualTo("1000");
+ assertThat(command.getEnvVariables().size()).isEqualTo(System.getenv().size() + 1);
+ }
+
+ @Test
+ public void addJavaOptions_adds_jvm_options() {
+ JavaCommand command = new JavaCommand(ProcessId.ELASTICSEARCH);
+ assertThat(command.getJavaOptions()).isEmpty();
+
+ command.addJavaOptions("");
+ assertThat(command.getJavaOptions()).isEmpty();
+
+ command.addJavaOptions("-Xmx512m -Xms256m -Dfoo");
+ assertThat(command.getJavaOptions()).containsOnly("-Xmx512m", "-Xms256m", "-Dfoo");
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.es;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.process.Props;
+import org.sonar.process.es.EsLogging;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class EsLoggingTest {
+ @Rule
+ public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ private EsLogging underTest = new EsLogging();
+
+ @Test
+ public void createProperties_with_empty_props() throws IOException {
+ File logDir = temporaryFolder.newFolder();
+ Properties properties = underTest.createProperties(newProps(), logDir);
+
+ verifyProperties(properties,
+ "status", "ERROR",
+ "appender.file_es.type", "RollingFile",
+ "appender.file_es.name", "file_es",
+ "appender.file_es.filePattern", new File(logDir, "es.%d{yyyy-MM-dd}.log").getAbsolutePath(),
+ "appender.file_es.fileName", new File(logDir, "es.log").getAbsolutePath(),
+ "appender.file_es.layout.type", "PatternLayout",
+ "appender.file_es.layout.pattern", "%d{yyyy.MM.dd HH:mm:ss} %-5level es[][%logger{1.}] %msg%n",
+ "appender.file_es.policies.type", "Policies",
+ "appender.file_es.policies.time.type", "TimeBasedTriggeringPolicy",
+ "appender.file_es.policies.time.interval", "1",
+ "appender.file_es.policies.time.modulate", "true",
+ "appender.file_es.strategy.type", "DefaultRolloverStrategy",
+ "appender.file_es.strategy.fileIndex", "nomax",
+ "appender.file_es.strategy.action.type", "Delete",
+ "appender.file_es.strategy.action.basepath", logDir.getAbsolutePath(),
+ "appender.file_es.strategy.action.maxDepth", "1",
+ "appender.file_es.strategy.action.condition.type", "IfFileName",
+ "appender.file_es.strategy.action.condition.glob", "es*",
+ "appender.file_es.strategy.action.condition.nested_condition.type", "IfAccumulatedFileCount",
+ "appender.file_es.strategy.action.condition.nested_condition.exceeds", "7",
+ "rootLogger.level", "INFO",
+ "rootLogger.appenderRef.file_es.ref", "file_es");
+ }
+
+ @Test
+ public void createProperties_sets_root_logger_to_INFO_if_no_property_is_set() throws IOException {
+ File logDir = temporaryFolder.newFolder();
+ Properties properties = underTest.createProperties(newProps(), logDir);
+
+ assertThat(properties.getProperty("rootLogger.level")).isEqualTo("INFO");
+ }
+
+ @Test
+ public void createProperties_sets_root_logger_to_global_property_if_set() throws IOException {
+ File logDir = temporaryFolder.newFolder();
+ Properties properties = underTest.createProperties(newProps("sonar.log.level", "TRACE"), logDir);
+
+ assertThat(properties.getProperty("rootLogger.level")).isEqualTo("TRACE");
+ }
+
+ @Test
+ public void createProperties_sets_root_logger_to_process_property_if_set() throws IOException {
+ File logDir = temporaryFolder.newFolder();
+ Properties properties = underTest.createProperties(newProps("sonar.log.level.es", "DEBUG"), logDir);
+
+ assertThat(properties.getProperty("rootLogger.level")).isEqualTo("DEBUG");
+ }
+
+ @Test
+ public void createProperties_sets_root_logger_to_process_property_over_global_property_if_both_set() throws IOException {
+ File logDir = temporaryFolder.newFolder();
+ Properties properties = underTest.createProperties(
+ newProps(
+ "sonar.log.level", "DEBUG",
+ "sonar.log.level.es", "TRACE"),
+ logDir);
+
+ assertThat(properties.getProperty("rootLogger.level")).isEqualTo("TRACE");
+ }
+
+ private static Props newProps(String... propertyKeysAndValues) {
+ assertThat(propertyKeysAndValues.length % 2).describedAs("Number of parameters must be even").isEqualTo(0);
+ Properties properties = new Properties();
+ for (int i = 0; i < propertyKeysAndValues.length; i++) {
+ properties.put(propertyKeysAndValues[i++], propertyKeysAndValues[i]);
+ }
+ return new Props(properties);
+ }
+
+ private void verifyProperties(Properties properties, String... expectedPropertyKeysAndValuesOrdered) {
+ if (expectedPropertyKeysAndValuesOrdered.length == 0) {
+ assertThat(properties.size()).isEqualTo(0);
+ } else {
+ assertThat(expectedPropertyKeysAndValuesOrdered.length % 2).describedAs("Number of parameters must be even").isEqualTo(0);
+ Set<String> keys = new HashSet<>(expectedPropertyKeysAndValuesOrdered.length / 2 + 1);
+ keys.add("status");
+ for (int i = 0; i < expectedPropertyKeysAndValuesOrdered.length; i++) {
+ String key = expectedPropertyKeysAndValuesOrdered[i++];
+ String value = expectedPropertyKeysAndValuesOrdered[i];
+ assertThat(properties.get(key)).describedAs("Unexpected value for property " + key).isEqualTo(value);
+ keys.add(key);
+ }
+ assertThat(properties.keySet()).containsOnly(keys.toArray());
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.es;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+import java.util.Properties;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.process.ProcessProperties;
+import org.sonar.process.Props;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class EsSettingsTest {
+
+ private static final boolean CLUSTER_ENABLED = true;
+ private static final boolean CLUSTER_DISABLED = false;
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void test_default_settings() throws Exception {
+ File homeDir = temp.newFolder();
+ Props props = new Props(new Properties());
+ props.set(ProcessProperties.SEARCH_PORT, "1234");
+ props.set(ProcessProperties.SEARCH_HOST, "127.0.0.1");
+ props.set(ProcessProperties.PATH_HOME, homeDir.getAbsolutePath());
+ props.set(ProcessProperties.CLUSTER_NAME, "sonarqube");
+
+ EsSettings esSettings = new EsSettings(props);
+
+ Map<String, String> generated = esSettings.build();
+ assertThat(generated.get("transport.tcp.port")).isEqualTo("1234");
+ assertThat(generated.get("transport.host")).isEqualTo("127.0.0.1");
+
+ // no cluster, but cluster and node names are set though
+ assertThat(generated.get("cluster.name")).isEqualTo("sonarqube");
+ assertThat(generated.get("node.name")).isEqualTo("sonarqube");
+
+ assertThat(generated.get("path.data")).isNotNull();
+ assertThat(generated.get("path.logs")).isNotNull();
+ assertThat(generated.get("path.home")).isNull();
+ assertThat(generated.get("path.conf")).isNotNull();
+
+ // http is disabled for security reasons
+ assertThat(generated.get("http.enabled")).isEqualTo("false");
+
+ assertThat(generated.get("discovery.zen.ping.unicast.hosts")).isNull();
+ assertThat(generated.get("discovery.zen.minimum_master_nodes")).isEqualTo("1");
+ assertThat(generated.get("discovery.initial_state_timeout")).isEqualTo("30s");
+
+ assertThat(generated.get("action.auto_create_index")).isEqualTo("false");
+ }
+
+ @Test
+ public void override_dirs() throws Exception {
+ File dataDir = temp.newFolder();
+ File logDir = temp.newFolder();
+ File tempDir = temp.newFolder();
+ Props props = minProps(CLUSTER_DISABLED);
+ props.set(ProcessProperties.PATH_DATA, dataDir.getAbsolutePath());
+ props.set(ProcessProperties.PATH_LOGS, logDir.getAbsolutePath());
+ props.set(ProcessProperties.PATH_TEMP, tempDir.getAbsolutePath());
+
+ Map<String, String> settings = new EsSettings(props).build();
+
+ assertThat(settings.get("path.data")).isEqualTo(new File(dataDir, "es").getAbsolutePath());
+ assertThat(settings.get("path.logs")).isEqualTo(logDir.getAbsolutePath());
+ assertThat(settings.get("path.home")).isNull();
+ assertThat(settings.get("path.conf")).isEqualTo(new File(tempDir, "conf/es").getAbsolutePath());
+ }
+
+ @Test
+ public void set_discovery_settings_if_cluster_is_enabled() throws Exception {
+ Props props = minProps(CLUSTER_ENABLED);
+ props.set(ProcessProperties.CLUSTER_SEARCH_HOSTS, "1.2.3.4:9000,1.2.3.5:8080");
+ Map<String, String> settings = new EsSettings(props).build();
+
+ assertThat(settings.get("discovery.zen.ping.unicast.hosts")).isEqualTo("1.2.3.4:9000,1.2.3.5:8080");
+ assertThat(settings.get("discovery.zen.minimum_master_nodes")).isEqualTo("2");
+ assertThat(settings.get("discovery.initial_state_timeout")).isEqualTo("120s");
+ }
+
+ @Test
+ public void incorrect_values_of_minimum_master_nodes() throws Exception {
+ Props props = minProps(CLUSTER_ENABLED);
+ props.set(ProcessProperties.SEARCH_MINIMUM_MASTER_NODES, "ꝱꝲꝳପ");
+
+ EsSettings underTest = new EsSettings(props);
+
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("Value of property sonar.search.minimumMasterNodes is not an integer:");
+ underTest.build();
+ }
+
+ @Test
+ public void cluster_is_enabled_with_defined_minimum_master_nodes() throws Exception {
+ Props props = minProps(CLUSTER_ENABLED);
+ props.set(ProcessProperties.SEARCH_MINIMUM_MASTER_NODES, "5");
+ Map<String, String> settings = new EsSettings(props).build();
+
+ assertThat(settings.get("discovery.zen.minimum_master_nodes")).isEqualTo("5");
+ }
+
+ @Test
+ public void cluster_is_enabled_with_defined_initialTimeout() throws Exception {
+ Props props = minProps(CLUSTER_ENABLED);
+ props.set(ProcessProperties.SEARCH_INITIAL_STATE_TIMEOUT, "10s");
+ Map<String, String> settings = new EsSettings(props).build();
+
+ assertThat(settings.get("discovery.initial_state_timeout")).isEqualTo("10s");
+ }
+
+ @Test
+ public void in_standalone_initialTimeout_is_not_overridable() throws Exception {
+ Props props = minProps(CLUSTER_DISABLED);
+ props.set(ProcessProperties.SEARCH_INITIAL_STATE_TIMEOUT, "10s");
+ Map<String, String> settings = new EsSettings(props).build();
+
+ assertThat(settings.get("discovery.initial_state_timeout")).isEqualTo("30s");
+ }
+
+ @Test
+ public void in_standalone_minimumMasterNodes_is_not_overridable() throws Exception {
+ Props props = minProps(CLUSTER_DISABLED);
+ props.set(ProcessProperties.SEARCH_MINIMUM_MASTER_NODES, "5");
+ Map<String, String> settings = new EsSettings(props).build();
+
+ assertThat(settings.get("discovery.zen.minimum_master_nodes")).isEqualTo("1");
+ }
+
+
+
+ @Test
+ public void enable_marvel() throws Exception {
+ Props props = minProps(CLUSTER_DISABLED);
+ props.set("sonar.search.marvelHosts", "127.0.0.2,127.0.0.3");
+ Map<String, String> settings = new EsSettings(props).build();
+
+ assertThat(settings.get("marvel.agent.exporter.es.hosts")).isEqualTo("127.0.0.2,127.0.0.3");
+ }
+
+ @Test
+ public void enable_http_connector() throws Exception {
+ Props props = minProps(CLUSTER_DISABLED);
+ props.set(ProcessProperties.SEARCH_HTTP_PORT, "9010");
+ Map<String, String> settings = new EsSettings(props).build();
+
+ assertThat(settings.get("http.port")).isEqualTo("9010");
+ assertThat(settings.get("http.host")).isEqualTo("127.0.0.1");
+ assertThat(settings.get("http.enabled")).isEqualTo("true");
+ }
+
+ @Test
+ public void enable_http_connector_different_host() throws Exception {
+ Props props = minProps(CLUSTER_DISABLED);
+ props.set(ProcessProperties.SEARCH_HTTP_PORT, "9010");
+ props.set(ProcessProperties.SEARCH_HOST, "127.0.0.2");
+ Map<String, String> settings = new EsSettings(props).build();
+
+ assertThat(settings.get("http.port")).isEqualTo("9010");
+ assertThat(settings.get("http.host")).isEqualTo("127.0.0.2");
+ assertThat(settings.get("http.enabled")).isEqualTo("true");
+ }
+
+ private Props minProps(boolean cluster) throws IOException {
+ File homeDir = temp.newFolder();
+ Props props = new Props(new Properties());
+ ProcessProperties.completeDefaults(props);
+ props.set(ProcessProperties.PATH_HOME, homeDir.getAbsolutePath());
+ props.set(ProcessProperties.CLUSTER_ENABLED, Boolean.toString(cluster));
+ return props;
+ }
+}
import org.sonar.application.config.AppSettings;
import org.sonar.application.config.AppSettingsLoader;
import org.sonar.application.config.AppSettingsLoaderImpl;
-import org.sonar.application.process.CommandFactory;
-import org.sonar.application.process.CommandFactoryImpl;
+import org.sonar.process.command.CommandFactory;
+import org.sonar.process.command.CommandFactoryImpl;
import org.sonar.application.process.ProcessLauncher;
import org.sonar.application.process.ProcessLauncherImpl;
import org.sonar.application.process.StopRequestWatcher;
try (AppState appState = new AppStateFactory(settings).create()) {
appState.registerSonarQubeVersion(getSonarqubeVersion());
AppReloader appReloader = new AppReloaderImpl(settingsLoader, fileSystem, appState, logging);
- CommandFactory commandFactory = new CommandFactoryImpl(settings);
+ CommandFactory commandFactory = new CommandFactoryImpl(settings.getProps());
fileSystem.reset();
try (ProcessLauncher processLauncher = new ProcessLauncherImpl(fileSystem.getTempDir())) {