deletion of temp directory is required for uninstalling plugins but also to cleanly start web server (eg. deleting unsuccessfully processed analysis reports)
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.monitor;
+
+import java.io.File;
+import java.io.IOException;
+
+public interface FileSystem {
+ void reset() throws IOException;
+
+ File getTempDir();
+}
private final File tempDir;
private final AllProcessesCommands allProcessesCommands;
- public JavaProcessLauncher(Timeouts timeouts, File tempDir, AllProcessesCommands allProcessesCommands) {
+ public JavaProcessLauncher(Timeouts timeouts, File tempDir) {
this.timeouts = timeouts;
this.tempDir = tempDir;
- this.allProcessesCommands = allProcessesCommands;
+ this.allProcessesCommands = new AllProcessesCommands(tempDir);
}
public void close() {
allProcessesCommands.close();
}
+ public ProcessCommands getProcessCommand(int processNumber, boolean clean) {
+ return allProcessesCommands.getProcessCommand(processNumber, clean);
+ }
+
ProcessRef launch(JavaCommand command) {
Process process = null;
try {
package org.sonar.process.monitor;
import java.io.File;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.sonar.process.AllProcessesCommands;
import org.sonar.process.Lifecycle;
import org.sonar.process.Lifecycle.State;
import org.sonar.process.ProcessCommands;
private static int restartorInstanceCounter = 0;
- private final AllProcessesCommands allProcessesCommands;
- private final JavaProcessLauncher launcher;
-
+ private final FileSystem fileSystem;
private final SystemExit systemExit;
+ private final boolean watchForHardStop;
private final Thread shutdownHook = new Thread(new MonitorShutdownHook(), "Monitor Shutdown Hook");
private final List<WatcherThread> watcherThreads = new CopyOnWriteArrayList<>();
private final Lifecycle lifecycle = new Lifecycle();
+
private final TerminatorThread terminator = new TerminatorThread();
private final RestartRequestWatcherThread restartWatcher = new RestartRequestWatcherThread();
@CheckForNull
private List<JavaCommand> javaCommands;
@CheckForNull
+ private JavaProcessLauncher launcher;
+ @CheckForNull
private RestartorThread restartor;
@CheckForNull
HardStopWatcherThread hardStopWatcher;
static int nextProcessId = 1;
- Monitor(File tempDir, SystemExit exit) {
- this.allProcessesCommands = new AllProcessesCommands(tempDir);
- this.launcher = new JavaProcessLauncher(TIMEOUTS, tempDir, allProcessesCommands);
+ Monitor(FileSystem fileSystem, SystemExit exit, boolean watchForHardStop) {
+ this.fileSystem = fileSystem;
this.systemExit = exit;
+ this.watchForHardStop = watchForHardStop;
}
- public static Monitor create(File tempDir) {
- return new Monitor(tempDir, new SystemExit());
+ public static Monitor create(FileSystem fileSystem, boolean watchForHardStop) {
+ return new Monitor(fileSystem, new SystemExit(), watchForHardStop);
}
/**
// intercepts CTRL-C
Runtime.getRuntime().addShutdownHook(shutdownHook);
+
+ // start watching for restart requested by child process
this.restartWatcher.start();
this.javaCommands = commands;
private void startProcesses() {
// do no start any child process if not in state INIT or RESTARTING (a stop could be in progress too)
if (lifecycle.tryToMoveTo(State.STARTING)) {
+ resetFileSystem();
+
startAndMonitorProcesses();
stopIfAnyProcessDidNotStart();
+ watchForHardStop();
+ }
+ }
+
+ private void watchForHardStop() {
+ if (!watchForHardStop) {
+ return;
+ }
+
+ if (this.hardStopWatcher != null) {
+ this.hardStopWatcher.stopWatching();
+ awaitTermination(this.hardStopWatcher);
+ }
+ ProcessCommands processCommand = this.launcher.getProcessCommand(CURRENT_PROCESS_NUMBER, true);
+ this.hardStopWatcher = new HardStopWatcherThread(processCommand);
+ this.hardStopWatcher.start();
+ }
+
+ private void resetFileSystem() {
+ // since JavaLauncher depends on temp directory, which is reset below, we need to close it first
+ closeJavaLauncher();
+ try {
+ fileSystem.reset();
+ } catch (IOException e) {
+ // failed to reset FileSystem
+ throw new RuntimeException("Failed to reset file system", e);
}
}
private void startAndMonitorProcesses() {
+ File tempDir = fileSystem.getTempDir();
+ this.launcher = new JavaProcessLauncher(TIMEOUTS, tempDir);
for (JavaCommand command : javaCommands) {
try {
ProcessRef processRef = launcher.launch(command);
}
}
- public void watchForHardStop() {
- ProcessCommands processCommand = this.allProcessesCommands.getProcessCommand(CURRENT_PROCESS_NUMBER, true);
- this.hardStopWatcher = new HardStopWatcherThread(processCommand);
- this.hardStopWatcher.start();
+ private void closeJavaLauncher() {
+ if (this.launcher != null) {
+ this.launcher.close();
+ this.launcher = null;
+ }
}
private void monitor(ProcessRef processRef) {
Runtime.getRuntime().removeShutdownHook(shutdownHook);
}
// cleanly close JavaLauncher
- launcher.close();
+ closeJavaLauncher();
}
}
}
private void stopAsync(State stoppingState) {
+ assert stoppingState == State.STOPPING || stoppingState == State.HARD_STOPPING;
if (lifecycle.tryToMoveTo(stoppingState)) {
terminator.start();
}
public class HardStopWatcherThread extends Thread {
private final ProcessCommands processCommands;
+ private boolean watch = true;
public HardStopWatcherThread(ProcessCommands processCommands) {
super("Hard stop watcher");
@Override
public void run() {
- while (lifecycle.getState() != Lifecycle.State.STOPPED) {
+ while (watch && lifecycle.getState() != Lifecycle.State.STOPPED) {
if (processCommands.askedForStop()) {
trace("Stopping process");
Monitor.this.stop();
}
}
+ public void stopWatching() {
+ this.watch = false;
+ }
}
private void stopProcesses() {
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
-import org.sonar.process.AllProcessesCommands;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
public void fail_to_launch() throws Exception {
File tempDir = temp.newFolder();
JavaCommand command = new JavaCommand("test");
- JavaProcessLauncher launcher = new JavaProcessLauncher(new Timeouts(), tempDir, new AllProcessesCommands(tempDir));
+ JavaProcessLauncher launcher = new JavaProcessLauncher(new Timeouts(), tempDir);
try {
// command is not correct (missing options), java.lang.ProcessBuilder#start()
// throws an exception
import org.sonar.process.ProcessCommands;
import org.sonar.process.SystemExit;
+import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import static org.sonar.process.monitor.MonitorTest.HttpProcessClientAssert.assertThat;
public class MonitorTest {
- static File testJar;
- Monitor monitor;
- SystemExit exit = mock(SystemExit.class);
+ private static File testJar;
+
+ private FileSystem fileSystem = mock(FileSystem.class);
+ private SystemExit exit = mock(SystemExit.class);
+
+ private Monitor underTest;
/**
* Find the JAR file containing the test apps. Classes can't be moved in sonar-process-monitor because
@After
public void tearDown() {
try {
- if (monitor != null) {
- monitor.stop();
+ if (underTest != null) {
+ underTest.stop();
}
} catch (Throwable ignored) {
}
@Test
public void fail_to_start_if_no_commands() throws Exception {
- monitor = newDefaultMonitor(tempDir);
+ underTest = newDefaultMonitor(tempDir);
try {
- monitor.start(Collections.<JavaCommand>emptyList());
+ underTest.start(Collections.<JavaCommand>emptyList());
fail();
} catch (IllegalArgumentException e) {
assertThat(e).hasMessage("At least one command is required");
@Test
public void fail_to_start_multiple_times() throws Exception {
- monitor = newDefaultMonitor(tempDir);
- monitor.start(Arrays.asList(newStandardProcessCommand()));
+ underTest = newDefaultMonitor(tempDir);
+ underTest.start(singletonList(newStandardProcessCommand()));
boolean failed = false;
try {
- monitor.start(Arrays.asList(newStandardProcessCommand()));
+ underTest.start(singletonList(newStandardProcessCommand()));
} catch (IllegalStateException e) {
failed = e.getMessage().equals("Can not start multiple times");
}
- monitor.stop();
+ underTest.stop();
assertThat(failed).isTrue();
}
@Test
public void start_then_stop_gracefully() throws Exception {
- monitor = newDefaultMonitor(tempDir);
+ underTest = newDefaultMonitor(tempDir);
HttpProcessClient client = new HttpProcessClient(tempDir, "test");
// blocks until started
- monitor.start(Arrays.asList(client.newCommand()));
+ underTest.start(singletonList(client.newCommand()));
assertThat(client).isReady()
.wasStartedBefore(System.currentTimeMillis());
// blocks until stopped
- monitor.stop();
+ underTest.stop();
assertThat(client)
.isNotReady()
.wasGracefullyTerminated();
- assertThat(monitor.getState()).isEqualTo(State.STOPPED);
+ assertThat(underTest.getState()).isEqualTo(State.STOPPED);
+ verify(fileSystem).reset();
}
@Test
public void start_then_stop_sequence_of_commands() throws Exception {
- monitor = newDefaultMonitor(tempDir);
+ underTest = newDefaultMonitor(tempDir);
HttpProcessClient p1 = new HttpProcessClient(tempDir, "p1");
HttpProcessClient p2 = new HttpProcessClient(tempDir, "p2");
- monitor.start(Arrays.asList(p1.newCommand(), p2.newCommand()));
+ underTest.start(Arrays.asList(p1.newCommand(), p2.newCommand()));
// start p2 when p1 is fully started (ready)
assertThat(p1)
.isReady()
.wasStartedBefore(p2);
assertThat(p2)
- .isReady();
+ .isReady();
- monitor.stop();
+ underTest.stop();
// stop in inverse order
assertThat(p1)
assertThat(p2)
.isNotReady()
.wasGracefullyTerminatedBefore(p1);
+ verify(fileSystem).reset();
}
@Test
public void stop_all_processes_if_monitor_shutdowns() throws Exception {
- monitor = newDefaultMonitor(tempDir);
+ underTest = newDefaultMonitor(tempDir);
HttpProcessClient p1 = new HttpProcessClient(tempDir, "p1");
HttpProcessClient p2 = new HttpProcessClient(tempDir, "p2");
- monitor.start(Arrays.asList(p1.newCommand(), p2.newCommand()));
+ underTest.start(Arrays.asList(p1.newCommand(), p2.newCommand()));
assertThat(p1).isReady();
assertThat(p2).isReady();
// emulate CTRL-C
- monitor.getShutdownHook().run();
- monitor.getShutdownHook().join();
+ underTest.getShutdownHook().run();
+ underTest.getShutdownHook().join();
assertThat(p1).wasGracefullyTerminated();
assertThat(p2).wasGracefullyTerminated();
+
+ verify(fileSystem).reset();
}
@Test
public void restart_all_processes_if_one_asks_for_restart() throws Exception {
- monitor = newDefaultMonitor(tempDir);
+ underTest = newDefaultMonitor(tempDir);
HttpProcessClient p1 = new HttpProcessClient(tempDir, "p1");
HttpProcessClient p2 = new HttpProcessClient(tempDir, "p2");
- monitor.start(Arrays.asList(p1.newCommand(), p2.newCommand()));
+ underTest.start(Arrays.asList(p1.newCommand(), p2.newCommand()));
assertThat(p1).isReady();
assertThat(p2).isReady();
p2.restart();
- assertThat(monitor.waitForOneRestart()).isTrue();
+ assertThat(underTest.waitForOneRestart()).isTrue();
assertThat(p1)
- .wasStarted(2)
- .wasGracefullyTerminated(1);
+ .wasStarted(2)
+ .wasGracefullyTerminated(1);
assertThat(p2)
- .wasStarted(2)
- .wasGracefullyTerminated(1);
+ .wasStarted(2)
+ .wasGracefullyTerminated(1);
- monitor.stop();
+ underTest.stop();
assertThat(p1)
- .wasStarted(2)
- .wasGracefullyTerminated(2);
+ .wasStarted(2)
+ .wasGracefullyTerminated(2);
assertThat(p2)
- .wasStarted(2)
- .wasGracefullyTerminated(2);
+ .wasStarted(2)
+ .wasGracefullyTerminated(2);
+
+ verify(fileSystem, times(2)).reset();
}
@Test
public void stop_all_processes_if_one_shutdowns() throws Exception {
- monitor = newDefaultMonitor(tempDir);
+ underTest = newDefaultMonitor(tempDir);
HttpProcessClient p1 = new HttpProcessClient(tempDir, "p1");
HttpProcessClient p2 = new HttpProcessClient(tempDir, "p2");
- monitor.start(Arrays.asList(p1.newCommand(), p2.newCommand()));
+ underTest.start(Arrays.asList(p1.newCommand(), p2.newCommand()));
assertThat(p1.isReady()).isTrue();
assertThat(p2.isReady()).isTrue();
// kill p1 -> waiting for detection by monitor than termination of p2
p1.kill();
- monitor.awaitTermination();
+ underTest.awaitTermination();
assertThat(p1)
.isNotReady()
assertThat(p2)
.isNotReady()
.wasGracefullyTerminated();
+
+ verify(fileSystem).reset();
}
@Test
public void stop_all_processes_if_one_fails_to_start() throws Exception {
- monitor = newDefaultMonitor(tempDir);
+ underTest = newDefaultMonitor(tempDir);
HttpProcessClient p1 = new HttpProcessClient(tempDir, "p1");
HttpProcessClient p2 = new HttpProcessClient(tempDir, "p2", -1);
try {
- monitor.start(Arrays.asList(p1.newCommand(), p2.newCommand()));
+ underTest.start(Arrays.asList(p1.newCommand(), p2.newCommand()));
fail();
} catch (Exception expected) {
assertThat(p1)
@Test
public void fail_to_start_if_bad_class_name() throws Exception {
- monitor = newDefaultMonitor(tempDir);
+ underTest = newDefaultMonitor(tempDir);
JavaCommand command = new JavaCommand("test")
.addClasspath(testJar.getAbsolutePath())
.setClassName("org.sonar.process.test.Unknown");
try {
- monitor.start(Arrays.asList(command));
+ underTest.start(singletonList(command));
fail();
} catch (Exception e) {
// expected
@Test
public void watchForHardStop_adds_a_hardStopWatcher_thread_and_starts_it() throws Exception {
- Monitor monitor = newDefaultMonitor(tempDir);
- assertThat(monitor.hardStopWatcher).isNull();
+ underTest = newDefaultMonitor(tempDir, true);
+ assertThat(underTest.hardStopWatcher).isNull();
- monitor.watchForHardStop();
+ HttpProcessClient p1 = new HttpProcessClient(tempDir, "p1");
+ underTest.start(singletonList(p1.newCommand()));
- assertThat(monitor.hardStopWatcher).isNotNull();
- assertThat(monitor.hardStopWatcher.isAlive()).isTrue();
+ assertThat(underTest.hardStopWatcher).isNotNull();
+ assertThat(underTest.hardStopWatcher.isAlive()).isTrue();
+
+ p1.kill();
+ underTest.awaitTermination();
+
+ assertThat(underTest.hardStopWatcher.isAlive()).isFalse();
}
private Monitor newDefaultMonitor(File tempDir) throws IOException {
- return new Monitor(tempDir, exit);
+ return newDefaultMonitor(tempDir, false);
+ }
+
+ private Monitor newDefaultMonitor(File tempDir, boolean watchForHardStop) throws IOException {
+ when(fileSystem.getTempDir()).thenReturn(tempDir);
+ return new Monitor(fileSystem, exit, watchForHardStop);
}
/**
public void restart() {
try {
HttpRequest httpRequest = HttpRequest.post("http://localhost:" + httpPort + "/" + "restart")
- .readTimeout(5000).connectTimeout(5000);
+ .readTimeout(5000).connectTimeout(5000);
if (!httpRequest.ok() || !"ok".equals(httpRequest.body())) {
throw new IllegalStateException("Wrong response calling restart");
}
private final Monitor monitor;
- public App(File tempDir) {
- this(Monitor.create(tempDir));
+ public App(AppFileSystem appFileSystem, boolean watchForHardStop) {
+ this(Monitor.create(appFileSystem, watchForHardStop));
}
App(Monitor monitor) {
}
public void start(Props props) {
- if (props.valueAsBoolean(ProcessProperties.ENABLE_STOP_COMMAND, false)) {
- monitor.watchForHardStop();
- }
monitor.start(createCommands(props));
monitor.awaitTermination();
}
- private List<JavaCommand> createCommands(Props props) {
+ private static List<JavaCommand> createCommands(Props props) {
List<JavaCommand> commands = new ArrayList<>();
File homeDir = props.nonNullValueAsFile(ProcessProperties.PATH_HOME);
JavaCommand elasticsearch = new JavaCommand("search");
CommandLineParser cli = new CommandLineParser();
Properties rawProperties = cli.parseArguments(args);
Props props = new PropsBuilder(rawProperties, new JdbcSettings()).build();
+ AppFileSystem appFileSystem = new AppFileSystem(props);
+ appFileSystem.verifyProps();
AppLogging logging = new AppLogging();
logging.configure(props);
- File tempDir = props.nonNullValueAsFile(ProcessProperties.PATH_TEMP);
- App app = new App(tempDir);
+ // used by orchestrator
+ boolean watchForHardStop = props.valueAsBoolean(ProcessProperties.ENABLE_STOP_COMMAND, false);
+ App app = new App(appFileSystem, watchForHardStop);
app.start(props);
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.process.Props;
+import org.sonar.process.monitor.FileSystem;
+
+import static org.apache.commons.io.FileUtils.deleteQuietly;
+import static org.apache.commons.io.FileUtils.forceMkdir;
+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 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");
+ }
+ ensureDirectoryExists(props, PATH_DATA);
+ ensureDirectoryExists(props, PATH_WEB);
+ ensureDirectoryExists(props, PATH_LOGS);
+ createOrCleanDirectory(props, PATH_TEMP);
+ }
+
+ @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 ensureDirectoryExists(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(String.format("Property '%s' is not valid, not a directory: %s",
+ propKey, dir.getAbsolutePath()));
+ }
+ }
+
+ private static void createOrCleanDirectory(Props props, String propKey) throws IOException {
+ File dir = props.nonNullValueAsFile(propKey);
+ LOG.info("Deleting and/or creating temp directory {}", dir.getAbsolutePath());
+ if (!ensureDirectoryExists(props, propKey)) {
+ deleteQuietly(dir);
+ forceMkdir(dir);
+ }
+ }
+}
*/
package org.sonar.application;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.IOUtils;
-import org.sonar.process.ConfigurationUtils;
-import org.sonar.process.ProcessProperties;
-import org.sonar.process.Props;
-
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.Properties;
+import org.apache.commons.io.IOUtils;
+import org.sonar.process.ConfigurationUtils;
+import org.sonar.process.ProcessProperties;
+import org.sonar.process.Props;
class PropsBuilder {
}
/**
- * Load optional conf/sonar.properties, interpolates environment variables and
- * initializes file system
+ * Load optional conf/sonar.properties, interpolates environment variables
*/
Props build() throws IOException {
Properties p = loadPropertiesFile(homeDir);
Props props = new Props(p);
ProcessProperties.completeDefaults(props);
- // init file system
- initExistingDir(props, ProcessProperties.PATH_DATA, "data");
- initExistingDir(props, ProcessProperties.PATH_WEB, "web");
- initExistingDir(props, ProcessProperties.PATH_LOGS, "logs");
- initTempDir(props);
-
// check JDBC properties and set path to driver
jdbcSettings.checkAndComplete(homeDir, props);
}
return p;
}
-
- private void initTempDir(Props props) throws IOException {
- File dir = configureDir(props, ProcessProperties.PATH_TEMP, "temp");
- FileUtils.deleteQuietly(dir);
- FileUtils.forceMkdir(dir);
- }
-
- private void initExistingDir(Props props, String propKey, String defaultRelativePath) throws IOException {
- File dir = configureDir(props, propKey, defaultRelativePath);
- if (!dir.exists()) {
- FileUtils.forceMkdir(dir);
- }
- if (!dir.isDirectory()) {
- throw new IllegalStateException(String.format("Property '%s' is not valid, not a directory: %s",
- propKey, dir.getAbsolutePath()));
- }
- }
-
- private File configureDir(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);
- }
- props.set(propKey, d.getAbsolutePath());
- return d;
- }
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.util.Properties;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.process.Props;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class AppFileSystemTest {
+
+ private static final String PROPERTY_SONAR_PATH_WEB = "sonar.path.web";
+ private static final String PROPERTY_SONAR_PATH_DATA = "sonar.path.data";
+ private static final String PROPERTY_SONAR_PATH_LOGS = "sonar.path.logs";
+ private static final String PROPERTY_SONAR_PATH_TEMP = "sonar.path.temp";
+ private static final String NON_DEFAULT_DATA_DIR_NAME = "toto";
+ private static final String NON_DEFAULT_WEB_DIR_NAME = "tutu";
+ private static final String NON_DEFAULT_LOGS_DIR_NAME = "titi";
+ private static final String NON_DEFAULT_TEMP_DIR_NAME = "tatta";
+ private static final String DEFAULT_DATA_DIR_NAME = "data";
+ private static final String DEFAULT_WEB_DIR_NAME = "web";
+ private static final String DEFAULT_LOGS_DIR_NAME = "logs";
+ private static final String DEFAULT_TEMP_DIR_NAME = "temp";
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private File homeDir;
+ private Properties properties;
+
+ @Before
+ public void before() throws IOException {
+ homeDir = temp.newFolder();
+
+ properties = new Properties();
+ properties.setProperty("sonar.path.home", homeDir.getAbsolutePath());
+ }
+
+ @Test
+ public void verifyProps_set_dir_path_absolute_based_on_home_dir_and_default_names_when_no_property() {
+ Props props = new Props(properties);
+ AppFileSystem underTest = new AppFileSystem(props);
+
+ underTest.verifyProps();
+
+ assertThat(props.nonNullValue(PROPERTY_SONAR_PATH_DATA)).isEqualTo(new File(homeDir, DEFAULT_DATA_DIR_NAME).getAbsolutePath());
+ assertThat(props.nonNullValue(PROPERTY_SONAR_PATH_WEB)).isEqualTo(new File(homeDir, DEFAULT_WEB_DIR_NAME).getAbsolutePath());
+ assertThat(props.nonNullValue(PROPERTY_SONAR_PATH_LOGS)).isEqualTo(new File(homeDir, DEFAULT_LOGS_DIR_NAME).getAbsolutePath());
+ assertThat(props.nonNullValue(PROPERTY_SONAR_PATH_TEMP)).isEqualTo(new File(homeDir, DEFAULT_TEMP_DIR_NAME).getAbsolutePath());
+ }
+
+ @Test
+ public void verifyProps_can_be_called_multiple_times() {
+ AppFileSystem underTest = new AppFileSystem(new Props(properties));
+
+ underTest.verifyProps();
+ underTest.verifyProps();
+ }
+
+ @Test
+ public void reset_throws_ISE_if_verifyProps_not_called_first() throws Exception {
+ AppFileSystem underTest = new AppFileSystem(new Props(properties));
+
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("method verifyProps must be called first");
+
+ underTest.reset();
+ }
+
+ @Test
+ public void verifyProps_makes_dir_path_absolute_based_on_home_dir_when_relative() throws Exception {
+ properties.setProperty(PROPERTY_SONAR_PATH_WEB, NON_DEFAULT_WEB_DIR_NAME);
+ properties.setProperty(PROPERTY_SONAR_PATH_DATA, NON_DEFAULT_DATA_DIR_NAME);
+ properties.setProperty(PROPERTY_SONAR_PATH_LOGS, NON_DEFAULT_LOGS_DIR_NAME);
+ properties.setProperty(PROPERTY_SONAR_PATH_TEMP, NON_DEFAULT_TEMP_DIR_NAME);
+
+ Props props = new Props(properties);
+ AppFileSystem underTest = new AppFileSystem(props);
+
+ underTest.verifyProps();
+
+ assertThat(props.nonNullValue(PROPERTY_SONAR_PATH_DATA)).isEqualTo(new File(homeDir, NON_DEFAULT_DATA_DIR_NAME).getAbsolutePath());
+ assertThat(props.nonNullValue(PROPERTY_SONAR_PATH_WEB)).isEqualTo(new File(homeDir, NON_DEFAULT_WEB_DIR_NAME).getAbsolutePath());
+ assertThat(props.nonNullValue(PROPERTY_SONAR_PATH_LOGS)).isEqualTo(new File(homeDir, NON_DEFAULT_LOGS_DIR_NAME).getAbsolutePath());
+ assertThat(props.nonNullValue(PROPERTY_SONAR_PATH_TEMP)).isEqualTo(new File(homeDir, NON_DEFAULT_TEMP_DIR_NAME).getAbsolutePath());
+ }
+
+ @Test
+ public void reset_creates_dir_all_dirs_if_they_don_t_exist() throws Exception {
+ AppFileSystem underTest = new AppFileSystem(new Props(properties));
+
+ underTest.verifyProps();
+
+ File dataDir = new File(homeDir, DEFAULT_DATA_DIR_NAME);
+ File webDir = new File(homeDir, DEFAULT_WEB_DIR_NAME);
+ File logsDir = new File(homeDir, DEFAULT_LOGS_DIR_NAME);
+ File tempDir = new File(homeDir, DEFAULT_TEMP_DIR_NAME);
+ assertThat(dataDir).doesNotExist();
+ assertThat(webDir).doesNotExist();
+ assertThat(logsDir).doesNotExist();
+ assertThat(tempDir).doesNotExist();
+
+ underTest.reset();
+
+ assertThat(dataDir).exists().isDirectory();
+ assertThat(webDir).exists().isDirectory();
+ assertThat(logsDir).exists().isDirectory();
+ assertThat(tempDir).exists().isDirectory();
+ }
+
+ @Test
+ public void reset_delete_temp_dir_if_already_exists() throws Exception {
+ File tempDir = new File(homeDir, DEFAULT_TEMP_DIR_NAME);
+ assertThat(tempDir.mkdir()).isTrue();
+ File fileInTempDir = new File(tempDir, "someFile.txt");
+ assertThat(fileInTempDir.createNewFile()).isTrue();
+
+ AppFileSystem underTest = new AppFileSystem(new Props(properties));
+ underTest.verifyProps();
+ underTest.reset();
+
+ assertThat(tempDir).exists();
+ assertThat(fileInTempDir).doesNotExist();
+ }
+
+ @Test
+ public void reset_throws_ISE_if_data_dir_is_a_file() throws Exception {
+ resetThrowsISEIfDirIsAFile(PROPERTY_SONAR_PATH_DATA);
+ }
+
+ @Test
+ public void reset_throws_ISE_if_web_dir_is_a_file() throws Exception {
+ resetThrowsISEIfDirIsAFile(PROPERTY_SONAR_PATH_WEB);
+ }
+
+ @Test
+ public void reset_throws_ISE_if_logs_dir_is_a_file() throws Exception {
+ resetThrowsISEIfDirIsAFile(PROPERTY_SONAR_PATH_LOGS);
+ }
+
+ @Test
+ public void reset_throws_ISE_if_temp_dir_is_a_file() throws Exception {
+ resetThrowsISEIfDirIsAFile(PROPERTY_SONAR_PATH_TEMP);
+ }
+
+ private void resetThrowsISEIfDirIsAFile(String property) throws IOException {
+ File file = new File(homeDir, "zoom.store");
+ assertThat(file.createNewFile()).isTrue();
+
+ properties.setProperty(property, "zoom.store");
+
+ AppFileSystem underTest = new AppFileSystem(new Props(properties));
+
+ underTest.verifyProps();
+
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("Property '" + property + "' is not valid, not a directory: " + file.getAbsolutePath());
+
+ underTest.reset();
+ }
+
+ // @Test
+// public void fail_if_required_directory_is_a_file() throws Exception {
+// // <home>/data is missing
+// FileUtils.forceMkdir(webDir);
+// FileUtils.forceMkdir(logsDir);
+// try {
+// FileUtils.touch(dataDir);
+// new PropsBuilder(new Properties(), jdbcSettings, homeDir).build();
+// fail();
+// } catch (IllegalStateException e) {
+// assertThat(e.getMessage()).startsWith("Property 'sonar.path.data' is not valid, not a directory: " + dataDir.getAbsolutePath());
+// }
+// }
+
+}
*/
package org.sonar.application;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Properties;
import org.apache.commons.io.FileUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.TemporaryFolder;
import org.sonar.process.Props;
-import java.io.File;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.Properties;
-
import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
public class PropsBuilderTest {
@Rule
public TemporaryFolder temp = new TemporaryFolder();
- File homeDir;
- File dataDir;
- File webDir;
- File logsDir;
- JdbcSettings jdbcSettings = mock(JdbcSettings.class);
+ private File homeDir;
+ private JdbcSettings jdbcSettings = mock(JdbcSettings.class);
@Before
public void before() throws IOException {
homeDir = temp.newFolder();
- dataDir = new File(homeDir, "data");
- webDir = new File(homeDir, "web");
- logsDir = new File(homeDir, "logs");
}
@Test
public void build_props() throws Exception {
- FileUtils.forceMkdir(dataDir);
- FileUtils.forceMkdir(webDir);
- FileUtils.forceMkdir(logsDir);
Properties rawProperties = new Properties();
rawProperties.setProperty("foo", "bar");
Props props = new PropsBuilder(rawProperties, jdbcSettings, homeDir).build();
- assertThat(props.nonNullValueAsFile("sonar.path.logs")).isEqualTo(logsDir);
- assertThat(props.nonNullValueAsFile("sonar.path.home")).isEqualTo(homeDir);
-
- // create <HOME>/temp
- File tempDir = props.nonNullValueAsFile("sonar.path.temp");
- assertThat(tempDir).isDirectory().exists();
- assertThat(tempDir.getName()).isEqualTo("temp");
- assertThat(tempDir.getParentFile()).isEqualTo(homeDir);
-
assertThat(props.value("foo")).isEqualTo("bar");
assertThat(props.value("unknown")).isNull();
assertThat(props.valueAsInt("sonar.search.port")).isEqualTo(9001);
}
- @Test
- public void create_missing_required_directory() throws Exception {
- // <home>/data is missing
- FileUtils.forceMkdir(webDir);
- FileUtils.forceMkdir(logsDir);
-
- File dataDir = new File(homeDir, "data");
- new PropsBuilder(new Properties(), jdbcSettings, homeDir).build();
- assertThat(dataDir).isDirectory().exists();
- }
-
- @Test
- public void fail_if_required_directory_is_a_file() throws Exception {
- // <home>/data is missing
- FileUtils.forceMkdir(webDir);
- FileUtils.forceMkdir(logsDir);
- try {
- FileUtils.touch(dataDir);
- new PropsBuilder(new Properties(), jdbcSettings, homeDir).build();
- fail();
- } catch (IllegalStateException e) {
- assertThat(e.getMessage()).startsWith("Property 'sonar.path.data' is not valid, not a directory: " + dataDir.getAbsolutePath());
- }
- }
-
@Test
public void load_properties_file_if_exists() throws Exception {
FileUtils.write(new File(homeDir, "conf/sonar.properties"), "sonar.jdbc.username=angela\nsonar.origin=file");
- FileUtils.forceMkdir(dataDir);
- FileUtils.forceMkdir(webDir);
- FileUtils.forceMkdir(logsDir);
Properties rawProperties = new Properties();
rawProperties.setProperty("sonar.origin", "raw");
@Test
public void do_not_load_properties_file_if_not_exists() throws Exception {
- FileUtils.forceMkdir(dataDir);
- FileUtils.forceMkdir(webDir);
- FileUtils.forceMkdir(logsDir);
-
Properties rawProperties = new Properties();
rawProperties.setProperty("sonar.origin", "raw");
Props props = new PropsBuilder(rawProperties, jdbcSettings, homeDir).build();