aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-application/src/main
diff options
context:
space:
mode:
authorSimon Brandhof <simon.brandhof@sonarsource.com>2017-03-10 15:01:01 +0100
committerSimon Brandhof <simon.brandhof@sonarsource.com>2017-03-13 13:54:03 +0100
commit6fa3d925c688fa8e67480c7a69ded9f86aba5326 (patch)
tree11f4baac39111ae6822f945faf344e88b99dec7c /sonar-application/src/main
parent857d12fa9909a5b5fde7a42f231b0b8d42e50303 (diff)
downloadsonarqube-6fa3d925c688fa8e67480c7a69ded9f86aba5326.tar.gz
sonarqube-6fa3d925c688fa8e67480c7a69ded9f86aba5326.zip
SONAR-8816 automatic election of web leader in cluster mode
Diffstat (limited to 'sonar-application/src/main')
-rw-r--r--sonar-application/src/main/java/org/sonar/application/App.java210
-rw-r--r--sonar-application/src/main/java/org/sonar/application/AppFileSystem.java186
-rw-r--r--sonar-application/src/main/java/org/sonar/application/AppLogging.java226
-rw-r--r--sonar-application/src/main/java/org/sonar/application/Cluster.java103
-rw-r--r--sonar-application/src/main/java/org/sonar/application/ClusterParameters.java65
-rw-r--r--sonar-application/src/main/java/org/sonar/application/ClusterProperties.java185
-rw-r--r--sonar-application/src/main/java/org/sonar/application/CommandLineParser.java60
-rw-r--r--sonar-application/src/main/java/org/sonar/application/JavaCommandFactory.java32
-rw-r--r--sonar-application/src/main/java/org/sonar/application/JavaCommandFactoryImpl.java114
-rw-r--r--sonar-application/src/main/java/org/sonar/application/JdbcSettings.java154
-rw-r--r--sonar-application/src/main/java/org/sonar/application/PropsBuilder.java92
11 files changed, 57 insertions, 1370 deletions
diff --git a/sonar-application/src/main/java/org/sonar/application/App.java b/sonar-application/src/main/java/org/sonar/application/App.java
index c1a7e61d566..efc4a89d234 100644
--- a/sonar-application/src/main/java/org/sonar/application/App.java
+++ b/sonar-application/src/main/java/org/sonar/application/App.java
@@ -19,172 +19,76 @@
*/
package org.sonar.application;
-import com.google.common.annotations.VisibleForTesting;
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Properties;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.function.Supplier;
-import org.apache.commons.io.FilenameUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.sonar.process.Lifecycle;
-import org.sonar.process.ProcessProperties;
-import org.sonar.process.Props;
-import org.sonar.process.Stoppable;
-import org.sonar.process.monitor.JavaCommand;
-import org.sonar.process.monitor.Monitor;
-
-import static org.sonar.process.Lifecycle.State;
-import static org.sonar.process.ProcessId.APP;
-
-/**
- * Entry-point of process that starts and monitors ElasticSearch, the Web Server and the Compute Engine.
- */
-public class App implements Stoppable {
-
- private final Properties commandLineArguments;
- private final Function<Properties, Props> propsSupplier;
- private final JavaCommandFactory javaCommandFactory;
- private final Monitor monitor;
- private final Supplier<List<JavaCommand>> javaCommandSupplier;
- private final Cluster cluster;
-
- private App(Properties commandLineArguments) {
- this.commandLineArguments = commandLineArguments;
- this.propsSupplier = properties -> new PropsBuilder(properties, new JdbcSettings()).build();
- this.javaCommandFactory = new JavaCommandFactoryImpl();
- Props props = propsSupplier.apply(commandLineArguments);
-
- AppFileSystem appFileSystem = new AppFileSystem(props);
- appFileSystem.verifyProps();
- ClusterProperties clusterProperties = new ClusterProperties(props);
- clusterProperties.populateProps(props);
- AppLogging logging = new AppLogging();
- logging.configure(props);
- clusterProperties.validate();
- this.cluster = new Cluster(clusterProperties);
-
- // used by orchestrator
- boolean watchForHardStop = props.valueAsBoolean(ProcessProperties.ENABLE_STOP_COMMAND, false);
- this.monitor = Monitor.newMonitorBuilder()
- .setProcessNumber(APP.getIpcIndex())
- .setFileSystem(appFileSystem)
- .setWatchForHardStop(watchForHardStop)
- .setWaitForOperational()
- .addListener(new AppLifecycleListener())
- .build();
- this.javaCommandSupplier = new ReloadableCommandSupplier(props, appFileSystem::ensureUnchangedConfiguration);
- }
-
- @VisibleForTesting
- App(Properties commandLineArguments, Function<Properties, Props> propsSupplier, Monitor monitor, CheckFSConfigOnReload checkFsConfigOnReload,
- JavaCommandFactory javaCommandFactory, Cluster cluster) {
- this.commandLineArguments = commandLineArguments;
- this.propsSupplier = propsSupplier;
- this.javaCommandFactory = javaCommandFactory;
- this.monitor = monitor;
- this.javaCommandSupplier = new ReloadableCommandSupplier(propsSupplier.apply(commandLineArguments), checkFsConfigOnReload);
- this.cluster = cluster;
- }
-
- public void start() throws InterruptedException {
- monitor.start(javaCommandSupplier);
- monitor.awaitTermination();
- }
-
- private static boolean isProcessEnabled(Props props, String disabledPropertyKey) {
- return !props.valueAsBoolean(ProcessProperties.CLUSTER_ENABLED) ||
- !props.valueAsBoolean(disabledPropertyKey);
- }
-
- static String starPath(File homeDir, String relativePath) {
- File dir = new File(homeDir, relativePath);
- return FilenameUtils.concat(dir.getAbsolutePath(), "*");
- }
-
- public static void main(String[] args) throws InterruptedException {
- CommandLineParser cli = new CommandLineParser();
- Properties rawProperties = cli.parseArguments(args);
-
- App app = new App(rawProperties);
- app.start();
- }
-
- @Override
- public void stopAsync() {
- if (cluster != null) {
- cluster.close();
- }
- if (monitor != null) {
- monitor.stop();
- }
- }
-
- private static class AppLifecycleListener implements Lifecycle.LifecycleListener {
- private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
-
- @Override
- public void successfulTransition(State from, State to) {
- if (to == State.OPERATIONAL) {
- LOGGER.info("SonarQube is up");
+import java.io.IOException;
+import org.sonar.application.config.AppSettings;
+import org.sonar.application.config.AppSettingsLoader;
+import org.sonar.application.config.AppSettingsLoaderImpl;
+import org.sonar.application.process.JavaCommandFactory;
+import org.sonar.application.process.JavaCommandFactoryImpl;
+import org.sonar.application.process.JavaProcessLauncher;
+import org.sonar.application.process.JavaProcessLauncherImpl;
+import org.sonar.application.process.StopRequestWatcher;
+import org.sonar.application.process.StopRequestWatcherImpl;
+import org.sonar.process.SystemExit;
+
+public class App {
+
+ private final SystemExit systemExit = new SystemExit();
+ private StopRequestWatcher stopRequestWatcher;
+
+ public void start(String[] cliArguments) throws IOException {
+ AppSettingsLoader settingsLoader = new AppSettingsLoaderImpl(cliArguments);
+ AppSettings settings = settingsLoader.load();
+ // order is important - logging must be configured before any other components (AppFileSystem, ...)
+ AppLogging logging = new AppLogging(settings);
+ logging.configure();
+ AppFileSystem fileSystem = new AppFileSystem(settings);
+
+ try (AppState appState = new AppStateFactory(settings).create()) {
+ AppReloader appReloader = new AppReloaderImpl(settingsLoader, fileSystem, appState, logging);
+ JavaCommandFactory javaCommandFactory = new JavaCommandFactoryImpl(settings);
+ fileSystem.reset();
+
+ try (JavaProcessLauncher javaProcessLauncher = new JavaProcessLauncherImpl(fileSystem.getTempDir())) {
+ Scheduler scheduler = new SchedulerImpl(settings, appReloader, javaCommandFactory, javaProcessLauncher, appState);
+
+ // intercepts CTRL-C
+ Runtime.getRuntime().addShutdownHook(new ShutdownHook(scheduler));
+
+ scheduler.schedule();
+
+ stopRequestWatcher = StopRequestWatcherImpl.create(settings, scheduler, fileSystem);
+ stopRequestWatcher.startWatching();
+
+ scheduler.awaitTermination();
+ stopRequestWatcher.stopWatching();
}
+ systemExit.exit(0);
}
}
- @FunctionalInterface
- interface CheckFSConfigOnReload extends Consumer<Props> {
-
+ public static void main(String... args) throws IOException {
+ new App().start(args);
}
- private class ReloadableCommandSupplier implements Supplier<List<JavaCommand>> {
- private final Props initialProps;
- private final CheckFSConfigOnReload checkFsConfigOnReload;
- private boolean initialPropsConsumed = false;
+ private class ShutdownHook extends Thread {
+ private final Scheduler scheduler;
- ReloadableCommandSupplier(Props initialProps, CheckFSConfigOnReload checkFsConfigOnReload) {
- this.initialProps = initialProps;
- this.checkFsConfigOnReload = checkFsConfigOnReload;
+ public ShutdownHook(Scheduler scheduler) {
+ super("SonarQube Shutdown Hook");
+ this.scheduler = scheduler;
}
@Override
- public List<JavaCommand> get() {
- if (!initialPropsConsumed) {
- initialPropsConsumed = true;
- return createCommands(this.initialProps);
- }
- return recreateCommands();
- }
-
- private List<JavaCommand> recreateCommands() {
- Props reloadedProps = propsSupplier.apply(commandLineArguments);
- AppFileSystem appFileSystem = new AppFileSystem(reloadedProps);
- appFileSystem.verifyProps();
- checkFsConfigOnReload.accept(reloadedProps);
- AppLogging logging = new AppLogging();
- logging.configure(reloadedProps);
-
- return createCommands(reloadedProps);
- }
-
- private List<JavaCommand> createCommands(Props props) {
- File homeDir = props.nonNullValueAsFile(ProcessProperties.PATH_HOME);
- List<JavaCommand> commands = new ArrayList<>(3);
- if (isProcessEnabled(props, ProcessProperties.CLUSTER_SEARCH_DISABLED)) {
- commands.add(javaCommandFactory.createESCommand(props, homeDir));
- }
-
- if (isProcessEnabled(props, ProcessProperties.CLUSTER_WEB_DISABLED)) {
- commands.add(javaCommandFactory.createWebCommand(props, homeDir));
- }
+ public void run() {
+ systemExit.setInShutdownHook();
- if (isProcessEnabled(props, ProcessProperties.CLUSTER_CE_DISABLED)) {
- commands.add(javaCommandFactory.createCeCommand(props, homeDir));
+ if (stopRequestWatcher != null) {
+ stopRequestWatcher.stopWatching();
}
- return commands;
+ // blocks until everything is corrected terminated
+ scheduler.terminate();
}
}
}
diff --git a/sonar-application/src/main/java/org/sonar/application/AppFileSystem.java b/sonar-application/src/main/java/org/sonar/application/AppFileSystem.java
deleted file mode 100644
index 9b07583314e..00000000000
--- a/sonar-application/src/main/java/org/sonar/application/AppFileSystem.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * 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;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.FileVisitOption;
-import java.nio.file.FileVisitResult;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.SimpleFileVisitor;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.util.EnumSet;
-import java.util.Objects;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.sonar.process.AllProcessesCommands;
-import org.sonar.process.Props;
-import org.sonar.process.monitor.FileSystem;
-
-import static java.lang.String.format;
-import static java.nio.file.FileVisitResult.CONTINUE;
-import static org.apache.commons.io.FileUtils.forceMkdir;
-import static org.sonar.process.FileUtils.deleteDirectory;
-import static org.sonar.process.ProcessProperties.PATH_DATA;
-import static org.sonar.process.ProcessProperties.PATH_HOME;
-import static org.sonar.process.ProcessProperties.PATH_LOGS;
-import static org.sonar.process.ProcessProperties.PATH_TEMP;
-import static org.sonar.process.ProcessProperties.PATH_WEB;
-
-public class AppFileSystem implements FileSystem {
- private static final Logger LOG = LoggerFactory.getLogger(AppFileSystem.class);
-
- private static final EnumSet<FileVisitOption> FOLLOW_LINKS = EnumSet.of(FileVisitOption.FOLLOW_LINKS);
- private static final String DEFAULT_DATA_DIRECTORY_NAME = "data";
- private static final String DEFAULT_WEB_DIRECTORY_NAME = "web";
- private static final String DEFAULT_LOGS_DIRECTORY_NAME = "logs";
- private static final String DEFAULT_TEMP_DIRECTORY_NAME = "temp";
-
- private final Props props;
- private final File homeDir;
- private boolean initialized = false;
-
- public AppFileSystem(Props props) {
- this.props = props;
- this.homeDir = props.nonNullValueAsFile(PATH_HOME);
- }
-
- public void verifyProps() {
- ensurePropertyIsAbsolutePath(props, PATH_DATA, DEFAULT_DATA_DIRECTORY_NAME);
- ensurePropertyIsAbsolutePath(props, PATH_WEB, DEFAULT_WEB_DIRECTORY_NAME);
- ensurePropertyIsAbsolutePath(props, PATH_LOGS, DEFAULT_LOGS_DIRECTORY_NAME);
- ensurePropertyIsAbsolutePath(props, PATH_TEMP, DEFAULT_TEMP_DIRECTORY_NAME);
- this.initialized = true;
- }
-
- /**
- * Must be called after {@link #verifyProps()}
- */
- @Override
- public void reset() throws IOException {
- if (!initialized) {
- throw new IllegalStateException("method verifyProps must be called first");
- }
- createDirectory(props, PATH_DATA);
- createDirectory(props, PATH_WEB);
- createDirectory(props, PATH_LOGS);
- File tempDir = createOrCleanTempDirectory(props, PATH_TEMP);
- try (AllProcessesCommands allProcessesCommands = new AllProcessesCommands(tempDir)) {
- allProcessesCommands.clean();
- }
- }
-
- @Override
- public File getTempDir() {
- return props.nonNullValueAsFile(PATH_TEMP);
- }
-
- private File ensurePropertyIsAbsolutePath(Props props, String propKey, String defaultRelativePath) {
- String path = props.value(propKey, defaultRelativePath);
- File d = new File(path);
- if (!d.isAbsolute()) {
- d = new File(homeDir, path);
- LOG.trace("Overriding property {} from relative path '{}' to absolute path '{}'", path, d.getAbsolutePath());
- props.set(propKey, d.getAbsolutePath());
- }
- return d;
- }
-
- private static boolean createDirectory(Props props, String propKey) throws IOException {
- File dir = props.nonNullValueAsFile(propKey);
- if (dir.exists()) {
- ensureIsNotAFile(propKey, dir);
- return false;
- } else {
- LOG.trace("forceMkdir {}", dir.getAbsolutePath());
- forceMkdir(dir);
- ensureIsNotAFile(propKey, dir);
- return true;
- }
- }
-
- private static void ensureIsNotAFile(String propKey, File dir) {
- if (!dir.isDirectory()) {
- throw new IllegalStateException(format("Property '%s' is not valid, not a directory: %s",
- propKey, dir.getAbsolutePath()));
- }
- }
-
- private static File createOrCleanTempDirectory(Props props, String propKey) throws IOException {
- File dir = props.nonNullValueAsFile(propKey);
- LOG.info("Cleaning or creating temp directory {}", dir.getAbsolutePath());
- if (!createDirectory(props, propKey)) {
- Files.walkFileTree(dir.toPath(), FOLLOW_LINKS, CleanTempDirFileVisitor.VISIT_MAX_DEPTH, new CleanTempDirFileVisitor(dir.toPath()));
- }
- return dir;
- }
-
- public void ensureUnchangedConfiguration(Props newProps) {
- verifyUnchanged(newProps, PATH_DATA, DEFAULT_DATA_DIRECTORY_NAME);
- verifyUnchanged(newProps, PATH_WEB, DEFAULT_WEB_DIRECTORY_NAME);
- verifyUnchanged(newProps, PATH_LOGS, DEFAULT_LOGS_DIRECTORY_NAME);
- verifyUnchanged(newProps, PATH_TEMP, DEFAULT_TEMP_DIRECTORY_NAME);
- }
-
- private void verifyUnchanged(Props newProps, String propKey, String defaultRelativePath) {
- String initialValue = props.value(propKey, defaultRelativePath);
- String newValue = newProps.value(propKey, defaultRelativePath);
- if (!Objects.equals(newValue, initialValue)) {
- throw new IllegalStateException(format("Change of property '%s' is not supported ('%s'=> '%s')", propKey, initialValue, newValue));
- }
- }
-
- private static class CleanTempDirFileVisitor extends SimpleFileVisitor<Path> {
- private static final Path SHAREDMEMORY_FILE = Paths.get("sharedmemory");
- public static final int VISIT_MAX_DEPTH = 1;
-
- private final Path path;
- private final boolean symLink;
-
- public CleanTempDirFileVisitor(Path path) {
- this.path = path;
- this.symLink = Files.isSymbolicLink(path);
- }
-
- @Override
- public FileVisitResult visitFile(Path filePath, BasicFileAttributes attrs) throws IOException {
- File file = filePath.toFile();
- if (file.isDirectory()) {
- deleteDirectory(file);
- } else if (filePath.getFileName().equals(SHAREDMEMORY_FILE)) {
- return CONTINUE;
- } else if (!symLink || !filePath.equals(path)) {
- Files.delete(filePath);
- }
- return CONTINUE;
- }
-
- @Override
- public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
- if (!dir.equals(path)) {
- deleteDirectory(dir.toFile());
- }
- return CONTINUE;
- }
- }
-
-}
diff --git a/sonar-application/src/main/java/org/sonar/application/AppLogging.java b/sonar-application/src/main/java/org/sonar/application/AppLogging.java
deleted file mode 100644
index 7ea96ff62bb..00000000000
--- a/sonar-application/src/main/java/org/sonar/application/AppLogging.java
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * 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;
-
-import ch.qos.logback.classic.Level;
-import ch.qos.logback.classic.Logger;
-import ch.qos.logback.classic.LoggerContext;
-import ch.qos.logback.classic.spi.ILoggingEvent;
-import ch.qos.logback.core.ConsoleAppender;
-import ch.qos.logback.core.FileAppender;
-import org.sonar.process.ProcessId;
-import org.sonar.process.Props;
-import org.sonar.process.logging.LogLevelConfig;
-import org.sonar.process.logging.LogbackHelper;
-import org.sonar.process.logging.RootLoggerConfig;
-
-import static org.slf4j.Logger.ROOT_LOGGER_NAME;
-import static org.sonar.process.logging.RootLoggerConfig.newRootLoggerConfigBuilder;
-import static org.sonar.process.monitor.StreamGobbler.LOGGER_GOBBLER;
-
-/**
- * Configure logback for the APP process.
- *
- * <p>
- * SonarQube's logging use cases:
- * <ol>
- * <li>
- * SQ started as a background process (with {@code sonar.sh start}):
- * <ul>
- * <li>
- * logs produced by the JVM before logback is setup in the APP JVM or which can't be caught by logback
- * (such as JVM crash) must be written to sonar.log
- * </li>
- * <li>
- * logs produced by the sub process JVMs before logback is setup in the subprocess JVMs or which can't be caught
- * by logback (such as JVM crash) must be written to sonar.log
- * </li>
- * <li>each JVM writes its own logs into its dedicated file</li>
- * </ul>
- * </li>
- * <li>
- * SQ started in console with wrapper (ie. with {@code sonar.sh console}):
- * <ul>
- * <li>
- * logs produced by the APP JVM before logback is setup in the APP JVM or which can't be caught by logback
- * (such as JVM crash) must be written to sonar.log
- * </li>
- * <li>
- * logs produced by the sub process JVMs before logback is setup in the subprocess JVMs or which can't be caught
- * by logback (such as JVM crash) must be written to sonar.log
- * </li>
- * <li>each JVM writes its own logs into its dedicated file</li>
- * <li>APP JVM logs are written to the APP JVM {@code System.out}</li>
- * </ul>
- * </li>
- * <li>
- * SQ started from command line (ie. {@code java -jar sonar-application-X.Y.jar}):
- * <ul>
- * <li>
- * logs produced by the APP JVM before logback is setup in the APP JVM or which can't be caught by logback
- * (such as JVM crash) are the responsibility of the user to be dealt with
- * </li>
- * <li>
- * logs produced by the sub process JVMs before logback is setup in the subprocess JVMs or which can't be caught
- * by logback (such as JVM crash) must be written to APP's {@code System.out}
- * </li>
- * <li>each JVM writes its own logs into its dedicated file</li>
- * <li>APP JVM logs are written to the APP JVM {@code System.out}</li>
- * </ul>
- * </li>
- * <li>
- * SQ started from an IT (ie. from command line with {@code option -Dsonar.log.console=true}):
- * <ul>
- * <li>
- * logs produced by the APP JVM before logback is setup in the APP JVM or which can't be caught by logback
- * (such as JVM crash) are the responsibility of the developer or maven to be dealt with
- * </li>
- * <li>
- * logs produced by the sub process JVMs before logback is setup in the subprocess JVMs or which can't be caught
- * by logback (such as JVM crash) must be written to APP's {@code System.out} and are the responsibility of the
- * developer or maven to be dealt with
- * </li>
- * <li>each JVM writes its own logs into its dedicated file</li>
- * <li>logs of all 4 JVMs are also written to the APP JVM {@code System.out}</li>
- * </ul>
- * </li>
- * </ol>
- * </p>
- *
- */
-class AppLogging {
-
- private static final String CONSOLE_LOGGER = "console";
- private static final String CONSOLE_PLAIN_APPENDER = "CONSOLE";
- private static final String APP_CONSOLE_APPENDER = "APP_CONSOLE";
- private static final String GOBBLER_PLAIN_CONSOLE = "GOBBLER_CONSOLE";
- private static final RootLoggerConfig APP_ROOT_LOGGER_CONFIG = newRootLoggerConfigBuilder()
- .setProcessId(ProcessId.APP)
- .build();
-
- private final LogbackHelper helper = new LogbackHelper();
-
- LoggerContext configure(Props props) {
- LoggerContext ctx = helper.getRootContext();
- ctx.reset();
-
- helper.enableJulChangePropagation(ctx);
-
- configureConsole(ctx);
- if (helper.isAllLogsToConsoleEnabled(props) || !props.valueAsBoolean("sonar.wrapped", false)) {
- configureWithLogbackWritingToFile(props, ctx);
- } else {
- configureWithWrapperWritingToFile(ctx);
- }
- helper.apply(
- LogLevelConfig.newBuilder()
- .rootLevelFor(ProcessId.APP)
- .immutableLevel("com.hazelcast", Level.toLevel(props.value(ClusterParameters.HAZELCAST_LOG_LEVEL.getName())))
- .build(), props);
-
- return ctx;
- }
-
- /**
- * Creates a non additive logger dedicated to printing message as is (ie. assuming they are already formatted).
- *
- * It creates a dedicated appender to the System.out which applies no formatting the logs it receives.
- */
- private void configureConsole(LoggerContext loggerContext) {
- ConsoleAppender<ILoggingEvent> consoleAppender = helper.newConsoleAppender(loggerContext, CONSOLE_PLAIN_APPENDER, "%msg%n");
-
- Logger consoleLogger = loggerContext.getLogger(CONSOLE_LOGGER);
- consoleLogger.setAdditive(false);
- consoleLogger.addAppender(consoleAppender);
- }
-
- /**
- * The process has been started by orchestrator (ie. via {@code java -jar} and optionally passing the option {@code -Dsonar.log.console=true}).
- * Therefor, APP's System.out (and System.err) are <strong>not</strong> copied to sonar.log by the wrapper and
- * printing to sonar.log must be done at logback level.
- */
- private void configureWithLogbackWritingToFile(Props props, LoggerContext ctx) {
- // configure all logs (ie. root logger) to be written to sonar.log and also to the console with formatting
- // in practice, this will be only APP's own logs as logs from sub processes LOGGER_GOBBLER and LOGGER_GOBBLER
- // is configured below to be detached from root
- // so, this will make all APP's log to be both written to sonar.log and visible in the console
- configureRootWithLogbackWritingToFile(props, ctx);
-
- // if option -Dsonar.log.console=true has been set, sub processes will write their logs to their own files but also
- // copy them to their System.out.
- // otherwise, the only logs to be expected in LOGGER_GOBBLER are those before logback is setup in subprocesses or
- // when their JVM crashes
- // they must be printed to App's System.out as is (as they are already formatted)
- // logger is configured to be non additive as we don't want these logs to be written to sonar.log and duplicated in
- // the console (with an incorrect formatting)
- configureGobbler(ctx);
- }
-
- /**
- * SQ has been started by the wrapper (ie. with sonar.sh) therefor, APP's System.out (and System.err) are written to
- * sonar.log by the wrapper.
- */
- private void configureWithWrapperWritingToFile(LoggerContext ctx) {
- // configure all logs (ie. root logger) to be written to console with formatting
- // in practice, this will be only APP's own logs as logs from sub processes are written to LOGGER_GOBBLER and
- // LOGGER_GOBBLER is configured below to be detached from root
- // logs are written to the console because we want them to be in sonar.log and the wrapper will write any log
- // from APP's System.out and System.err to sonar.log
- Logger rootLogger = ctx.getLogger(ROOT_LOGGER_NAME);
- rootLogger.addAppender(createAppConsoleAppender(ctx, helper.buildLogPattern(APP_ROOT_LOGGER_CONFIG)));
-
- // in regular configuration, sub processes are not copying their logs to their System.out, so, the only logs to be
- // expected in LOGGER_GOBBLER are those before logback is setup in subprocesses or when JVM crashes
- // so, they must be printed to App's System.out as is (as they are already formatted) and the wrapper will write
- // them to sonar.log
- // logger is configured to be non additive as we don't want these logs written to sonar.log and duplicated in the
- // console with an incorrect formatting
- configureGobbler(ctx);
- }
-
- private void configureRootWithLogbackWritingToFile(Props props, LoggerContext ctx) {
- Logger rootLogger = ctx.getLogger(ROOT_LOGGER_NAME);
- String appLogPattern = helper.buildLogPattern(APP_ROOT_LOGGER_CONFIG);
- FileAppender<ILoggingEvent> fileAppender = helper.newFileAppender(ctx, props, APP_ROOT_LOGGER_CONFIG, appLogPattern);
- rootLogger.addAppender(fileAppender);
- rootLogger.addAppender(createAppConsoleAppender(ctx, appLogPattern));
- }
-
- /**
- * Configure the logger to which logs from sub processes are written to
- * (called {@link org.sonar.process.monitor.StreamGobbler#LOGGER_GOBBLER}) by {@link org.sonar.process.monitor.StreamGobbler},
- * to be:
- * <ol>
- * <li>non additive (ie. these logs will be output by the appender of {@link org.sonar.process.monitor.StreamGobbler#LOGGER_GOBBLER} and only this one)</li>
- * <li>write logs as is (ie. without any extra formatting)</li>
- * <li>write exclusively to App's System.out</li>
- * </ol>
- */
- private void configureGobbler(LoggerContext ctx) {
- Logger gobblerLogger = ctx.getLogger(LOGGER_GOBBLER);
- gobblerLogger.setAdditive(false);
- gobblerLogger.addAppender(helper.newConsoleAppender(ctx, GOBBLER_PLAIN_CONSOLE, "%msg%n"));
- }
-
- private ConsoleAppender<ILoggingEvent> createAppConsoleAppender(LoggerContext ctx, String appLogPattern) {
- return helper.newConsoleAppender(ctx, APP_CONSOLE_APPENDER, appLogPattern);
- }
-
-}
diff --git a/sonar-application/src/main/java/org/sonar/application/Cluster.java b/sonar-application/src/main/java/org/sonar/application/Cluster.java
deleted file mode 100644
index 621af696320..00000000000
--- a/sonar-application/src/main/java/org/sonar/application/Cluster.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * 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;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.hazelcast.cluster.ClusterState;
-import com.hazelcast.config.Config;
-import com.hazelcast.config.JoinConfig;
-import com.hazelcast.config.NetworkConfig;
-import com.hazelcast.core.Hazelcast;
-import com.hazelcast.core.HazelcastInstance;
-import javax.annotation.Nonnull;
-
-/**
- * Manager for the cluster communication between Main Processes
- */
-public class Cluster implements AutoCloseable {
- /**
- * The Hazelcast instance.
- */
- @VisibleForTesting
- final HazelcastInstance hazelcastInstance;
-
- /**
- * Instantiates a new Cluster.
- *
- * @param clusterProperties The properties of the cluster read from configuration
- */
- protected Cluster(@Nonnull ClusterProperties clusterProperties) {
- if (clusterProperties.isEnabled()) {
- Config hzConfig = new Config();
- // Configure the network instance
- NetworkConfig netConfig = hzConfig.getNetworkConfig();
- netConfig.setPort(clusterProperties.getPort())
- .setPortAutoIncrement(clusterProperties.isPortAutoincrement());
-
- if (!clusterProperties.getInterfaces().isEmpty()) {
- netConfig.getInterfaces()
- .setEnabled(true)
- .setInterfaces(clusterProperties.getInterfaces());
- }
-
- // Only allowing TCP/IP configuration
- JoinConfig joinConfig = netConfig.getJoin();
- joinConfig.getAwsConfig().setEnabled(false);
- joinConfig.getMulticastConfig().setEnabled(false);
- joinConfig.getTcpIpConfig().setEnabled(true);
- joinConfig.getTcpIpConfig().setMembers(clusterProperties.getMembers());
-
- // Tweak HazelCast configuration
- hzConfig
- // Increase the number of tries
- .setProperty("hazelcast.tcp.join.port.try.count", "10")
- // Don't bind on all interfaces
- .setProperty("hazelcast.socket.bind.any", "false")
- // Don't phone home
- .setProperty("hazelcast.phone.home.enabled", "false")
- // Use slf4j for logging
- .setProperty("hazelcast.logging.type", "slf4j");
-
- // We are not using the partition group of Hazelcast, so disabling it
- hzConfig.getPartitionGroupConfig().setEnabled(false);
-
- hazelcastInstance = Hazelcast.newHazelcastInstance(hzConfig);
- } else {
- hazelcastInstance = null;
- }
- }
-
- /**
- * Is the cluster active
- *
- * @return the boolean
- */
- public boolean isActive() {
- return hazelcastInstance != null && hazelcastInstance.getCluster().getClusterState() == ClusterState.ACTIVE;
- }
-
- @Override
- public void close() {
- if (hazelcastInstance != null) {
- hazelcastInstance.shutdown();
- }
- }
-}
diff --git a/sonar-application/src/main/java/org/sonar/application/ClusterParameters.java b/sonar-application/src/main/java/org/sonar/application/ClusterParameters.java
deleted file mode 100644
index c0e15e0ca1b..00000000000
--- a/sonar-application/src/main/java/org/sonar/application/ClusterParameters.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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;
-
-import javax.annotation.Nonnull;
-import org.apache.commons.lang.StringUtils;
-
-enum ClusterParameters {
- ENABLED("sonar.cluster.enabled", Boolean.FALSE.toString()),
- MEMBERS("sonar.cluster.members", ""),
- PORT("sonar.cluster.port", Integer.toString(9003)),
- PORT_AUTOINCREMENT("sonar.cluster.port_autoincrement", Boolean.FALSE.toString()),
- INTERFACES("sonar.cluster.interfaces", ""),
- NAME("sonar.cluster.name", ""),
- HAZELCAST_LOG_LEVEL("sonar.log.level.app.hazelcast", "WARN");
-
- private final String name;
- private final String defaultValue;
-
- ClusterParameters(@Nonnull String name, @Nonnull String defaultValue) {
- this.name = name;
- this.defaultValue = defaultValue;
- }
-
- String getName() {
- return name;
- }
-
- String getDefaultValue() {
- return defaultValue;
- }
-
- boolean getDefaultValueAsBoolean() {
- return "true".equalsIgnoreCase(defaultValue);
- }
-
- Integer getDefaultValueAsInt() {
- if (StringUtils.isNotEmpty(defaultValue)) {
- try {
- return Integer.parseInt(defaultValue);
- } catch (NumberFormatException e) {
- throw new IllegalStateException("Default value of property " + name + " is not an integer: " + defaultValue, e);
- }
- }
- return null;
- }
-}
diff --git a/sonar-application/src/main/java/org/sonar/application/ClusterProperties.java b/sonar-application/src/main/java/org/sonar/application/ClusterProperties.java
deleted file mode 100644
index 1b86838ac5c..00000000000
--- a/sonar-application/src/main/java/org/sonar/application/ClusterProperties.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * 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;
-
-import java.net.InetAddress;
-import java.net.NetworkInterface;
-import java.net.SocketException;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.stream.Collectors;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import org.apache.commons.lang.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.sonar.process.Props;
-
-/**
- * Properties of the cluster configuration
- */
-final class ClusterProperties {
- private static final Logger LOGGER = LoggerFactory.getLogger(ClusterProperties.class);
-
- private final int port;
- private final boolean enabled;
- private final boolean portAutoincrement;
- private final List<String> members;
- private final List<String> interfaces;
- private final String name;
- private final String logLevel;
-
- ClusterProperties(@Nonnull Props props) {
- port = props.valueAsInt(ClusterParameters.PORT.getName(), ClusterParameters.PORT.getDefaultValueAsInt());
- enabled = props.valueAsBoolean(ClusterParameters.ENABLED.getName(), ClusterParameters.ENABLED.getDefaultValueAsBoolean());
- portAutoincrement = props.valueAsBoolean(ClusterParameters.PORT_AUTOINCREMENT.getName(), ClusterParameters.PORT_AUTOINCREMENT.getDefaultValueAsBoolean());
- interfaces = extractInterfaces(
- props.value(ClusterParameters.INTERFACES.getName(), ClusterParameters.INTERFACES.getDefaultValue())
- );
- name = props.value(ClusterParameters.NAME.getName(), ClusterParameters.NAME.getDefaultValue());
- logLevel = props.value(ClusterParameters.HAZELCAST_LOG_LEVEL.getName(), ClusterParameters.HAZELCAST_LOG_LEVEL.getDefaultValue());
- members = extractMembers(
- props.value(ClusterParameters.MEMBERS.getName(), ClusterParameters.MEMBERS.getDefaultValue())
- );
- }
-
- void populateProps(@Nonnull Props props) {
- props.set(ClusterParameters.PORT.getName(), Integer.toString(port));
- props.set(ClusterParameters.ENABLED.getName(), Boolean.toString(enabled));
- props.set(ClusterParameters.PORT_AUTOINCREMENT.getName(), Boolean.toString(portAutoincrement));
- props.set(ClusterParameters.INTERFACES.getName(), interfaces.stream().collect(Collectors.joining(",")));
- props.set(ClusterParameters.NAME.getName(), name);
- props.set(ClusterParameters.HAZELCAST_LOG_LEVEL.getName(), logLevel);
- props.set(ClusterParameters.MEMBERS.getName(), members.stream().collect(Collectors.joining(",")));
- }
-
- int getPort() {
- return port;
- }
-
- boolean isEnabled() {
- return enabled;
- }
-
- boolean isPortAutoincrement() {
- return portAutoincrement;
- }
-
- List<String> getMembers() {
- return members;
- }
-
- List<String> getInterfaces() {
- return interfaces;
- }
-
- String getName() {
- return name;
- }
-
- String getLogLevel() {
- return logLevel;
- }
-
- void validate() {
- if (!enabled) {
- return;
- }
- // Name is required in cluster mode
- checkArgument(
- StringUtils.isNotEmpty(name),
- "Cluster have been enabled but a %s has not been defined.",
- ClusterParameters.NAME.getName()
- );
-
- // Test validity of port
- checkArgument(
- port > 0 && port < 65_536,
- "Cluster port have been set to %d which is outside the range [1-65535].",
- port
- );
-
- // Test the interfaces parameter
- try {
- List<String> localInterfaces = findAllLocalIPs();
-
- interfaces.forEach(
- inet -> checkArgument(
- StringUtils.isEmpty(inet) || localInterfaces.contains(inet),
- "Interface %s is not available on this machine.",
- inet
- )
- );
- } catch (SocketException e) {
- LOGGER.warn("Unable to retrieve network interfaces. Interfaces won't be checked", e);
- }
- }
-
- private static List<String> extractMembers(final String members) {
- List<String> result = new ArrayList<>();
- for (String member : members.split(",")) {
- if (StringUtils.isNotEmpty(member)) {
- if (!member.contains(":")) {
- result.add(
- String.format("%s:%s", member, ClusterParameters.PORT.getDefaultValue())
- );
- } else {
- result.add(member);
- }
- }
- }
- return result;
- }
-
- private static List<String> extractInterfaces(final String interfaces) {
- List<String> result = new ArrayList<>();
- for (String iface : interfaces.split(",")) {
- if (StringUtils.isNotEmpty(iface)) {
- result.add(iface);
- }
- }
- return result;
- }
-
- private static List<String> findAllLocalIPs() throws SocketException {
- Enumeration<NetworkInterface> netInterfaces = NetworkInterface.getNetworkInterfaces();
- List<String> localInterfaces = new ArrayList<>();
-
- while (netInterfaces.hasMoreElements()) {
- NetworkInterface networkInterface = netInterfaces.nextElement();
- Enumeration<InetAddress> ips = networkInterface.getInetAddresses();
- while (ips.hasMoreElements()) {
- InetAddress ip = ips.nextElement();
- localInterfaces.add(ip.getHostAddress());
- }
- }
- return localInterfaces;
- }
-
- private static void checkArgument(boolean expression,
- @Nullable String messageTemplate,
- @Nullable Object... args) {
- if (!expression) {
- throw new IllegalArgumentException(String.format(messageTemplate, args));
- }
- }
-}
diff --git a/sonar-application/src/main/java/org/sonar/application/CommandLineParser.java b/sonar-application/src/main/java/org/sonar/application/CommandLineParser.java
deleted file mode 100644
index 35642d22c71..00000000000
--- a/sonar-application/src/main/java/org/sonar/application/CommandLineParser.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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;
-
-import org.apache.commons.lang.StringUtils;
-
-import java.util.Map;
-import java.util.Properties;
-
-class CommandLineParser {
- /**
- * Build properties from command-line arguments and system properties
- */
- Properties parseArguments(String[] args) {
- Properties props = argumentsToProperties(args);
-
- // complete with only the system properties that start with "sonar."
- for (Map.Entry<Object, Object> entry : System.getProperties().entrySet()) {
- String key = entry.getKey().toString();
- if (key.startsWith("sonar.")) {
- props.setProperty(key, entry.getValue().toString());
- }
- }
- return props;
- }
-
- /**
- * Convert strings "-Dkey=value" to properties
- */
- Properties argumentsToProperties(String[] args) {
- Properties props = new Properties();
- for (String arg : args) {
- if (!arg.startsWith("-D") || !arg.contains("=")) {
- throw new IllegalArgumentException(String.format(
- "Command-line argument must start with -D, for example -Dsonar.jdbc.username=sonar. Got: %s", arg));
- }
- String key = StringUtils.substringBefore(arg, "=").substring(2);
- String value = StringUtils.substringAfter(arg, "=");
- props.setProperty(key, value);
- }
- return props;
- }
-}
diff --git a/sonar-application/src/main/java/org/sonar/application/JavaCommandFactory.java b/sonar-application/src/main/java/org/sonar/application/JavaCommandFactory.java
deleted file mode 100644
index 0071a3619d8..00000000000
--- a/sonar-application/src/main/java/org/sonar/application/JavaCommandFactory.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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;
-
-import java.io.File;
-import org.sonar.process.Props;
-import org.sonar.process.monitor.JavaCommand;
-
-public interface JavaCommandFactory {
- JavaCommand createESCommand(Props props, File homeDir);
-
- JavaCommand createWebCommand(Props props, File homeDir);
-
- JavaCommand createCeCommand(Props props, File homeDir);
-}
diff --git a/sonar-application/src/main/java/org/sonar/application/JavaCommandFactoryImpl.java b/sonar-application/src/main/java/org/sonar/application/JavaCommandFactoryImpl.java
deleted file mode 100644
index 8058beb1018..00000000000
--- a/sonar-application/src/main/java/org/sonar/application/JavaCommandFactoryImpl.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * 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;
-
-import java.io.File;
-import org.sonar.process.ProcessId;
-import org.sonar.process.ProcessProperties;
-import org.sonar.process.Props;
-import org.sonar.process.monitor.JavaCommand;
-
-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 JavaCommandFactoryImpl implements JavaCommandFactory {
- /**
- * 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"};
-
- @Override
- public JavaCommand createESCommand(Props props, File workDir) {
- return newJavaCommand(ProcessId.ELASTICSEARCH, props, workDir)
- .addJavaOptions("-Djava.awt.headless=true")
- .addJavaOptions(props.nonNullValue(ProcessProperties.SEARCH_JAVA_OPTS))
- .addJavaOptions(props.nonNullValue(ProcessProperties.SEARCH_JAVA_ADDITIONAL_OPTS))
- .setClassName("org.sonar.search.SearchServer")
- .addClasspath("./lib/common/*")
- .addClasspath("./lib/search/*");
- }
-
- @Override
- public JavaCommand createWebCommand(Props props, File workDir) {
- JavaCommand command = newJavaCommand(ProcessId.WEB_SERVER, props, workDir)
- .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))
- .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(Props props, File workDir) {
- JavaCommand command = newJavaCommand(ProcessId.COMPUTE_ENGINE, props, workDir)
- .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 static JavaCommand newJavaCommand(ProcessId id, Props props, File workDir) {
- JavaCommand command = new JavaCommand(id)
- .setWorkDir(workDir)
- .setArguments(props.rawProperties());
-
- for (String key : PROXY_PROPERTY_KEYS) {
- if (props.contains(key)) {
- command.addJavaOption("-D" + key + "=" + props.value(key));
- }
- }
- // defaults of HTTPS are the same than HTTP defaults
- setSystemPropertyToDefaultIfNotSet(command, props, HTTPS_PROXY_HOST, HTTP_PROXY_HOST);
- setSystemPropertyToDefaultIfNotSet(command, props, HTTPS_PROXY_PORT, HTTP_PROXY_PORT);
- return command;
- }
-
- private static void setSystemPropertyToDefaultIfNotSet(JavaCommand command, Props props, String httpsProperty, String httpProperty) {
- if (!props.contains(httpsProperty) && props.contains(httpProperty)) {
- command.addJavaOption("-D" + httpsProperty + "=" + props.value(httpProperty));
- }
- }
-}
diff --git a/sonar-application/src/main/java/org/sonar/application/JdbcSettings.java b/sonar-application/src/main/java/org/sonar/application/JdbcSettings.java
deleted file mode 100644
index 82b137a6b02..00000000000
--- a/sonar-application/src/main/java/org/sonar/application/JdbcSettings.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * 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;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.lang.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.sonar.process.MessageException;
-import org.sonar.process.ProcessProperties;
-import org.sonar.process.Props;
-
-import static org.apache.commons.lang.StringUtils.isEmpty;
-import static org.apache.commons.lang.StringUtils.isNotEmpty;
-import static org.sonar.api.database.DatabaseProperties.PROP_EMBEDDED_PORT;
-import static org.sonar.api.database.DatabaseProperties.PROP_EMBEDDED_PORT_DEFAULT_VALUE;
-import static org.sonar.api.database.DatabaseProperties.PROP_URL;
-import static org.sonar.process.ProcessProperties.JDBC_URL;
-
-public class JdbcSettings {
-
- enum Provider {
- H2("lib/jdbc/h2"), SQLSERVER("lib/jdbc/mssql"), MYSQL("lib/jdbc/mysql"), ORACLE("extensions/jdbc-driver/oracle"),
- POSTGRESQL("lib/jdbc/postgresql");
-
- final String path;
-
- Provider(String path) {
- this.path = path;
- }
- }
-
- public void checkAndComplete(File homeDir, Props props) {
- Provider provider = resolveProviderAndEnforceNonnullJdbcUrl(props);
- String url = props.value(JDBC_URL);
- checkUrlParameters(provider, url);
- String driverPath = driverPath(homeDir, provider);
- props.set(ProcessProperties.JDBC_DRIVER_PATH, driverPath);
- }
-
- String driverPath(File homeDir, Provider provider) {
- String dirPath = provider.path;
- File dir = new File(homeDir, dirPath);
- if (!dir.exists()) {
- throw new MessageException("Directory does not exist: " + dirPath);
- }
- List<File> files = new ArrayList<>(FileUtils.listFiles(dir, new String[] {"jar"}, false));
- if (files.isEmpty()) {
- throw new MessageException("Directory does not contain JDBC driver: " + dirPath);
- }
- if (files.size() > 1) {
- throw new MessageException("Directory must contain only one JAR file: " + dirPath);
- }
- return files.get(0).getAbsolutePath();
- }
-
- Provider resolveProviderAndEnforceNonnullJdbcUrl(Props props) {
- String url = props.value(JDBC_URL);
- String embeddedDatabasePort = props.value(PROP_EMBEDDED_PORT);
-
- if (isNotEmpty(embeddedDatabasePort)) {
- String correctUrl = buildH2JdbcUrl(embeddedDatabasePort);
- warnIfUrlIsSet(embeddedDatabasePort, url, correctUrl);
- props.set(PROP_URL, correctUrl);
- return Provider.H2;
- }
-
- if (isEmpty(url)) {
- props.set(PROP_URL, buildH2JdbcUrl(PROP_EMBEDDED_PORT_DEFAULT_VALUE));
- props.set(PROP_EMBEDDED_PORT, PROP_EMBEDDED_PORT_DEFAULT_VALUE);
- return Provider.H2;
- }
-
- Pattern pattern = Pattern.compile("jdbc:(\\w+):.+");
- Matcher matcher = pattern.matcher(url);
- if (!matcher.find()) {
- throw new MessageException(String.format("Bad format of JDBC URL: %s", url));
- }
- String key = matcher.group(1);
- try {
- return Provider.valueOf(StringUtils.upperCase(key));
- } catch (IllegalArgumentException e) {
- throw new MessageException(String.format("Unsupported JDBC driver provider: %s", key));
- }
- }
-
- private static String buildH2JdbcUrl(String embeddedDatabasePort) {
- return "jdbc:h2:tcp://localhost:" + embeddedDatabasePort + "/sonar";
- }
-
- void checkUrlParameters(Provider provider, String url) {
- if (Provider.MYSQL.equals(provider)) {
- checkRequiredParameter(url, "useUnicode=true");
- checkRequiredParameter(url, "characterEncoding=utf8");
- checkRecommendedParameter(url, "rewriteBatchedStatements=true");
- checkRecommendedParameter(url, "useConfigs=maxPerformance");
- }
- }
-
- private static void warnIfUrlIsSet(String port, String existing, String expectedUrl) {
- if (isNotEmpty(existing)) {
- Logger logger = LoggerFactory.getLogger(JdbcSettings.class);
- if (expectedUrl.equals(existing)) {
- logger.warn("To change H2 database port, only property '{}' should be set (which current value is '{}'). " +
- "Remove property '{}' from configuration to remove this warning.",
- PROP_EMBEDDED_PORT, port,
- PROP_URL);
- } else {
- logger.warn("Both '{}' and '{}' properties are set. " +
- "The value of property '{}' ('{}') is not consistent with the value of property '{}' ('{}'). " +
- "The value of property '{}' will be ignored and value '{}' will be used instead. " +
- "To remove this warning, either remove property '{}' if your intent was to use the embedded H2 database, otherwise remove property '{}'.",
- PROP_EMBEDDED_PORT, PROP_URL,
- PROP_URL, existing, PROP_EMBEDDED_PORT, port,
- PROP_URL, expectedUrl,
- PROP_URL, PROP_EMBEDDED_PORT);
- }
- }
- }
-
- private static void checkRequiredParameter(String url, String val) {
- if (!url.contains(val)) {
- throw new MessageException(String.format("JDBC URL must have the property '%s'", val));
- }
- }
-
- private void checkRecommendedParameter(String url, String val) {
- if (!url.contains(val)) {
- LoggerFactory.getLogger(getClass()).warn("JDBC URL is recommended to have the property '{}'", val);
- }
- }
-}
diff --git a/sonar-application/src/main/java/org/sonar/application/PropsBuilder.java b/sonar-application/src/main/java/org/sonar/application/PropsBuilder.java
deleted file mode 100644
index 32f371dce83..00000000000
--- a/sonar-application/src/main/java/org/sonar/application/PropsBuilder.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * 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;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.net.URISyntaxException;
-import java.nio.charset.StandardCharsets;
-import java.util.Properties;
-import org.sonar.process.ConfigurationUtils;
-import org.sonar.process.ProcessProperties;
-import org.sonar.process.Props;
-
-class PropsBuilder {
-
- private final File homeDir;
- private final JdbcSettings jdbcSettings;
- private final Properties rawProperties;
-
- PropsBuilder(Properties rawProperties, JdbcSettings jdbcSettings, File homeDir) {
- this.rawProperties = rawProperties;
- this.jdbcSettings = jdbcSettings;
- this.homeDir = homeDir;
- }
-
- PropsBuilder(Properties rawProperties, JdbcSettings jdbcSettings) {
- this(rawProperties, jdbcSettings, detectHomeDir());
- }
-
- /**
- * Load optional conf/sonar.properties, interpolates environment variables
- */
- Props build() {
- Properties p = loadPropertiesFile(homeDir);
- p.putAll(rawProperties);
- p.setProperty(ProcessProperties.PATH_HOME, homeDir.getAbsolutePath());
- p = ConfigurationUtils.interpolateVariables(p, System.getenv());
-
- // the difference between Properties and Props is that the latter
- // supports decryption of values, so it must be used when values
- // are accessed
- Props props = new Props(p);
- ProcessProperties.completeDefaults(props);
-
- // check JDBC properties and set path to driver
- jdbcSettings.checkAndComplete(homeDir, props);
-
- return props;
- }
-
- static File detectHomeDir() {
- try {
- File appJar = new File(PropsBuilder.class.getProtectionDomain().getCodeSource().getLocation().toURI());
- return appJar.getParentFile().getParentFile();
- } catch (URISyntaxException e) {
- throw new IllegalStateException("Cannot detect path of main jar file", e);
- }
- }
-
- private static Properties loadPropertiesFile(File homeDir) {
- Properties p = new Properties();
- File propsFile = new File(homeDir, "conf/sonar.properties");
- if (propsFile.exists()) {
- try (Reader reader = new InputStreamReader(new FileInputStream(propsFile), StandardCharsets.UTF_8)) {
- p.load(reader);
- } catch (IOException e) {
- throw new IllegalStateException("Cannot open file " + propsFile, e);
- }
- }
- return p;
- }
-}