<artifactId>jsr305</artifactId>
<scope>provided</scope>
</dependency>
+
+ <!-- unit tests -->
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sonar-testing-harness</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
--- /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.ce;
+
+/**
+ * The Compute Engine program.
+ */
+public interface ComputeEngine {
+
+ /**
+ * @throws IllegalStateException when called more than once
+ */
+ void startup();
+
+ /**
+ * @throws IllegalStateException if {@link #startup()} has never been called
+ * @throws IllegalStateException when called more than once
+ */
+ void shutdown();
+}
--- /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.ce;
+
+import org.sonar.process.Props;
+
+import static com.google.common.base.Preconditions.checkState;
+
+public class ComputeEngineImpl implements ComputeEngine {
+ private volatile Status status = Status.INIT;
+
+ private final Props props;
+
+ public ComputeEngineImpl(Props props) {
+ this.props = props;
+ }
+
+ @Override
+ public void startup() {
+ checkStateAtStartup(this.status);
+ try {
+ this.status = Status.STARTING;
+ if (props.value("sonar.ce.startupFailure") != null) {
+ throw new IllegalStateException("Startup failed!");
+ }
+ } finally {
+ this.status = Status.STARTED;
+ }
+ }
+
+ private static void checkStateAtStartup(Status currentStatus) {
+ checkState(currentStatus == Status.INIT, "startup() can not be called multiple times");
+ }
+
+ @Override
+ public void shutdown() {
+ checkStateAsShutdown(this.status);
+ try {
+ this.status = Status.STOPPING;
+ if (props.value("sonar.ce.shutdownFailure") != null) {
+ throw new IllegalStateException("Shutdown failed!");
+ }
+ } finally {
+ this.status = Status.STOPPED;
+ }
+ }
+
+ private static void checkStateAsShutdown(Status currentStatus) {
+ checkState(currentStatus.ordinal() >= Status.STARTED.ordinal(), "shutdown() must not be called before startup()");
+ checkState(currentStatus.ordinal() <= Status.STOPPING.ordinal(), "shutdown() can not be called multiple times");
+ }
+
+ private enum Status {
+ INIT, STARTING, STARTED, STOPPING, STOPPED
+ }
+}
--- /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.ce.app;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.concurrent.atomic.AtomicReference;
+import javax.annotation.CheckForNull;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.ce.ComputeEngine;
+import org.sonar.ce.ComputeEngineImpl;
+import org.sonar.process.MinimumViableSystem;
+import org.sonar.process.Monitored;
+import org.sonar.process.ProcessEntryPoint;
+import org.sonar.process.Props;
+import org.sonar.server.app.ServerProcessLogging;
+
+import static com.google.common.base.Preconditions.checkState;
+import static org.sonar.process.ProcessUtils.awaitTermination;
+
+/**
+ * The Compute Engine server which starts a daemon thread to run the {@link ComputeEngineImpl} when it's {@link #start()}
+ * method is called.
+ * <p>
+ * This is the class to call to run a standalone {@link ComputeEngineImpl} (see {@link #main(String[])}).
+ * </p>
+ */
+public class CeServer implements Monitored {
+ private static final Logger LOG = Loggers.get(CeServer.class);
+
+ private static final String PROCESS_NAME = "ce";
+ private static final String CE_MAIN_THREAD_NAME = "ce-main";
+ private static final String LOG_LEVEL_PROPERTY = "sonar.log.level";
+
+ /**
+ * Thread that currently is inside our await() method.
+ */
+ private AtomicReference<Thread> awaitThread = new AtomicReference<>();
+ private volatile boolean stopAwait = false;
+
+ private final ComputeEngine computeEngine;
+ @CheckForNull
+ private CeMainThread ceMainThread = null;
+
+ public CeServer(ComputeEngine computeEngine) {
+ this.computeEngine = computeEngine;
+ new MinimumViableSystem()
+ .checkJavaVersion()
+ .checkWritableTempDir()
+ .checkRequiredJavaOptions(ImmutableMap.of("file.encoding", "UTF-8"));
+ }
+
+ /**
+ * Can't be started as is. Needs to be bootstrapped by sonar-application
+ */
+ public static void main(String[] args) throws Exception {
+ ProcessEntryPoint entryPoint = ProcessEntryPoint.createForArguments(args);
+ Props props = entryPoint.getProps();
+ new ServerProcessLogging(PROCESS_NAME, LOG_LEVEL_PROPERTY).configure(props);
+ CeServer server = new CeServer(new ComputeEngineImpl(props));
+ entryPoint.launch(server);
+ }
+
+ @Override
+ public void start() {
+ checkState(ceMainThread == null, "start() can not be called twice");
+ // start main thread
+ ceMainThread = new CeMainThread();
+ ceMainThread.start();
+ }
+
+ @Override
+ public boolean isReady() {
+ checkState(ceMainThread != null, "isReady() can not be called before start()");
+
+ return ceMainThread.isStarted();
+ }
+
+ @Override
+ public void awaitStop() {
+ checkState(awaitThread.compareAndSet(null, Thread.currentThread()), "There can't be more than one thread waiting for the Compute Engine Server to stop");
+ checkState(ceMainThread != null, "awaitStop() must not be called before start()");
+
+ try {
+ while (!stopAwait) {
+ try {
+ // wait for a quite long time but we will be interrupted if flag changes anyway
+ Thread.sleep(10000);
+ } catch (InterruptedException e) {
+ // continue and check the flag
+ }
+ }
+ } finally {
+ awaitThread = null;
+ }
+ }
+
+ @Override
+ public void stop() {
+ if (ceMainThread != null) {
+ // signal main Thread to stop
+ ceMainThread.stopIt();
+ awaitTermination(ceMainThread);
+ }
+ }
+
+ private void stopAwait() {
+ stopAwait = true;
+ Thread t = awaitThread.get();
+ if (t != null) {
+ t.interrupt();
+ try {
+ t.join(1000);
+ } catch (InterruptedException e) {
+ // Ignored
+ }
+ }
+ }
+
+ private class CeMainThread extends Thread {
+ private static final int CHECK_FOR_STOP_DELAY = 50;
+ private volatile boolean stop = false;
+ private volatile boolean started = false;
+
+ public CeMainThread() {
+ super(CE_MAIN_THREAD_NAME);
+ }
+
+ @Override
+ public void run() {
+ boolean startupSuccessful = attemptStartup();
+ this.started = true;
+ if (startupSuccessful) {
+ waitForStopSignal();
+ } else {
+ stopAwait();
+ }
+ }
+
+ private boolean attemptStartup() {
+ try {
+ startup();
+ return true;
+ } catch (Throwable e) {
+ LOG.error("Compute Engine Server startup failed", e);
+ return false;
+ }
+ }
+
+ private void startup() {
+ LOG.info("Compute Engine Server starting up...");
+ computeEngine.startup();
+ }
+
+ private void waitForStopSignal() {
+ while (!stop) {
+ try {
+ Thread.sleep(CHECK_FOR_STOP_DELAY);
+ } catch (InterruptedException e) {
+ // Ignored, check the flag
+ }
+ }
+ attemptShutdown();
+ }
+
+ private void attemptShutdown() {
+ try {
+ shutdown();
+ } catch (Throwable e) {
+ LOG.error("Compute Engine Server shutdown failed", e);
+ } finally {
+ // release thread waiting for CeServer
+ stopAwait();
+ }
+ }
+
+ private void shutdown() {
+ LOG.info("Compute Engine Server shutting down...");
+ computeEngine.shutdown();
+ }
+
+ public boolean isStarted() {
+ return started;
+ }
+
+ public void stopIt() {
+ this.stop = true;
+ }
+ }
+}
--- /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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.ce.app;
+
+import javax.annotation.ParametersAreNonnullByDefault;
--- /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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.ce;
+
+import javax.annotation.ParametersAreNonnullByDefault;
--- /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.ce;
+
+import java.util.Properties;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.process.Props;
+
+public class ComputeEngineImplTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private ComputeEngine underTest = new ComputeEngineImpl(new Props(new Properties()));
+
+ @Test
+ public void startup_throws_ISE_when_called_twice() {
+ underTest.startup();
+
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("startup() can not be called multiple times");
+
+ underTest.startup();
+ }
+
+ @Test
+ public void shutdown_throws_ISE_if_startup_was_not_called_before() {
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("shutdown() must not be called before startup()");
+
+ underTest.shutdown();
+ }
+
+ @Test
+ public void shutdown_throws_ISE_if_called_twice() {
+ underTest.startup();
+ underTest.shutdown();
+
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("shutdown() can not be called multiple times");
+
+ underTest.shutdown();
+ }
+}
--- /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.ce.app;
+
+import com.google.common.base.Objects;
+import java.util.concurrent.CountDownLatch;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.Timeout;
+import org.sonar.ce.ComputeEngine;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class CeServerTest {
+ @Rule
+ public Timeout timeout = Timeout.seconds(5);
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private CeServer underTest = null;
+ private Thread waitingThread = null;
+
+ @After
+ public void tearDown() throws Exception {
+ if (underTest != null) {
+ underTest.stop();
+ }
+ Thread waitingThread = this.waitingThread;
+ this.waitingThread = null;
+ if (waitingThread != null) {
+ waitingThread.join();
+ }
+ }
+
+ @Test
+ public void constructor_does_not_start_a_new_Thread() {
+ int activeCount = Thread.activeCount();
+
+ newCeServer();
+
+ assertThat(Thread.activeCount()).isSameAs(activeCount);
+ }
+
+ @Test
+ public void start_starts_a_new_Thread() {
+ int activeCount = Thread.activeCount();
+
+ newCeServer().start();
+
+ assertThat(Thread.activeCount()).isSameAs(activeCount + 1);
+ }
+
+ @Test
+ public void start_throws_ISE_when_called_twice() {
+ CeServer ceServer = newCeServer();
+
+ ceServer.start();
+
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("start() can not be called twice");
+
+ ceServer.start();
+ }
+
+ @Test
+ public void isReady_throws_ISE_when_called_before_start() {
+ CeServer ceServer = newCeServer();
+
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("isReady() can not be called before start()");
+
+ ceServer.isReady();
+ }
+
+ @Test
+ public void isReady_does_not_return_true_until_ComputeEngine_startup_returns() throws InterruptedException {
+ BlockingStartupComputeEngine computeEngine = new BlockingStartupComputeEngine(null);
+ CeServer ceServer = newCeServer(computeEngine);
+
+ ceServer.start();
+
+ assertThat(ceServer.isReady()).isFalse();
+
+ // release ComputeEngine startup method
+ computeEngine.releaseStartup();
+
+ while (!ceServer.isReady()) {
+ // wait for isReady to change to true, otherwise test will fail with timeout
+ }
+ assertThat(ceServer.isReady()).isTrue();
+ }
+
+ @Test
+ public void isReady_returns_true_when_ComputeEngine_startup_throws_any_Exception_or_Error() throws InterruptedException {
+ Throwable startupException = new Throwable("Faking failing ComputeEngine#startup()");
+
+ BlockingStartupComputeEngine computeEngine = new BlockingStartupComputeEngine(startupException);
+ CeServer ceServer = newCeServer(computeEngine);
+
+ ceServer.start();
+
+ assertThat(ceServer.isReady()).isFalse();
+
+ // release ComputeEngine startup method which will throw startupException
+ computeEngine.releaseStartup();
+
+ while (!ceServer.isReady()) {
+ // wait for isReady to change to true, otherwise test will fail with timeout
+ }
+ assertThat(ceServer.isReady()).isTrue();
+ }
+
+ @Test
+ public void awaitStop_throws_ISE_if_called_before_start() {
+ CeServer ceServer = newCeServer();
+
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("awaitStop() must not be called before start()");
+
+ ceServer.awaitStop();
+ }
+
+ @Test
+ public void awaitStop_throws_ISE_if_called_twice() throws InterruptedException {
+ final CeServer ceServer = newCeServer();
+ ExceptionCatcherWaitingThread waitingThread1 = new ExceptionCatcherWaitingThread(ceServer);
+ ExceptionCatcherWaitingThread waitingThread2 = new ExceptionCatcherWaitingThread(ceServer);
+
+ ceServer.start();
+
+ waitingThread1.start();
+ waitingThread2.start();
+
+ while (waitingThread1.isAlive() && waitingThread2.isAlive()) {
+ // wait for either thread to stop because ceServer.awaitStop() failed with an exception
+ // if none stops, the test will fail with timeout
+ }
+
+ Exception exception = Objects.firstNonNull(waitingThread1.getException(), waitingThread2.getException());
+ assertThat(exception)
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("There can't be more than one thread waiting for the Compute Engine Server to stop");
+
+ assertThat(waitingThread1.getException() != null && waitingThread2.getException() != null).isFalse();
+ }
+
+ @Test
+ public void awaitStop_keeps_blocking_calling_thread_even_if_calling_thread_is_interrupted_but_until_stop_is_called() throws InterruptedException {
+ final CeServer ceServer = newCeServer();
+ Thread waitingThread = newWaitingThread(new Runnable() {
+ @Override
+ public void run() {
+ ceServer.awaitStop();
+ }
+ });
+
+ ceServer.start();
+ waitingThread.start();
+
+ // interrupts waitingThread 5 times in a row (we really insist)
+ for (int i = 0; i < 5; i++) {
+ waitingThread.interrupt();
+ Thread.sleep(5);
+ assertThat(waitingThread.isAlive()).isTrue();
+ }
+
+ ceServer.stop();
+ // wait for waiting thread to stop because we stopped ceServer
+ // if it does not, the test will fail with timeout
+ waitingThread.join();
+ }
+
+ @Test
+ public void stop_releases_thread_in_awaitStop_even_when_ComputeEngine_shutdown_fails() throws InterruptedException {
+ final CeServer ceServer = newCeServer(new ComputeEngine() {
+ @Override
+ public void startup() {
+ // nothing to do at startup
+ }
+
+ @Override
+ public void shutdown() {
+ throw new Error("Faking ComputeEngine.shutdown() failing");
+ }
+ });
+ Thread waitingThread = newWaitingThread(new Runnable() {
+ @Override
+ public void run() {
+ ceServer.awaitStop();
+ }
+ });
+
+ ceServer.start();
+ waitingThread.start();
+ ceServer.stop();
+ // wait for waiting thread to stop because we stopped ceServer
+ // if it does not, the test will fail with timeout
+ waitingThread.join();
+ }
+
+ private CeServer newCeServer() {
+ return newCeServer(new ComputeEngine() {
+ @Override
+ public void startup() {
+ // do nothing
+ }
+
+ @Override
+ public void shutdown() {
+ // do nothing
+ }
+ });
+ }
+
+ private CeServer newCeServer(ComputeEngine computeEngine) {
+ checkState(this.underTest == null, "Only one CeServer can be created per test method");
+ this.underTest = new CeServer(computeEngine);
+ return underTest;
+ }
+
+ private Thread newWaitingThread(Runnable runnable) {
+ Thread t = new Thread(runnable);
+ checkState(this.waitingThread == null, "Only one waiting thread can be created per test method");
+ this.waitingThread = t;
+ return t;
+ }
+
+ private static class BlockingStartupComputeEngine implements ComputeEngine {
+ private final CountDownLatch latch = new CountDownLatch(1);
+ @CheckForNull
+ private final Throwable throwable;
+
+ public BlockingStartupComputeEngine(@Nullable Throwable throwable) {
+ this.throwable = throwable;
+ }
+
+ @Override
+ public void startup() {
+ try {
+ latch.await(1000, MILLISECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException("await failed", e);
+ }
+ if (throwable != null) {
+ if (throwable instanceof Error) {
+ throw (Error) throwable;
+ } else if (throwable instanceof RuntimeException) {
+ throw (RuntimeException) throwable;
+ }
+ }
+ }
+
+ @Override
+ public void shutdown() {
+ // do nothing
+ }
+
+ private void releaseStartup() {
+ this.latch.countDown();
+ }
+ }
+
+ private static class ExceptionCatcherWaitingThread extends Thread {
+ private final CeServer ceServer;
+ @CheckForNull
+ private Exception exception = null;
+
+ public ExceptionCatcherWaitingThread(CeServer ceServer) {
+ this.ceServer = ceServer;
+ }
+
+ @Override
+ public void run() {
+ try {
+ ceServer.awaitStop();
+ } catch (Exception e) {
+ this.exception = e;
+ }
+ }
+
+ @CheckForNull
+ public Exception getException() {
+ return exception;
+ }
+ }
+
+}
import org.sonar.process.Props;
public class WebServer implements Monitored {
+ private static final String LOG_LEVEL_PROPERTY = "sonar.log.level";
private final EmbeddedTomcat tomcat;
public static void main(String[] args) throws Exception {
ProcessEntryPoint entryPoint = ProcessEntryPoint.createForArguments(args);
Props props = entryPoint.getProps();
- new ServerProcessLogging().configure(props);
+ new ServerProcessLogging("web", LOG_LEVEL_PROPERTY).configure(props);
WebServer server = new WebServer(props);
entryPoint.launch(server);
}
<scope>provided</scope>
</dependencySet>
+ <dependencySet>
+ <outputDirectory>lib/ce</outputDirectory>
+ <useProjectArtifact>false</useProjectArtifact>
+ <useTransitiveDependencies>false</useTransitiveDependencies>
+ <useTransitiveFiltering>false</useTransitiveFiltering>
+ <includes>
+ <include>org.sonarsource.sonarqube:sonar-ce</include>
+ </includes>
+ <scope>provided</scope>
+ </dependencySet>
<dependencySet>
<outputDirectory>lib/batch</outputDirectory>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sonar-ce</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>sonar-scanner-engine-shaded</artifactId>
<configuration>
<rules>
<requireFilesSize>
- <minsize>105000000</minsize>
- <maxsize>125000000</maxsize>
+ <minsize>110000000</minsize>
+ <maxsize>120000000</maxsize>
<files>
<file>${project.build.directory}/sonarqube-${project.version}.zip</file>
</files>
}
private static List<JavaCommand> createCommands(Props props) {
- List<JavaCommand> commands = new ArrayList<>();
File homeDir = props.nonNullValueAsFile(ProcessProperties.PATH_HOME);
+ List<JavaCommand> commands = new ArrayList<>(3);
+ commands.add(createESCommand(props, homeDir));
+
+ // do not yet start WebServer nor CE on elasticsearch slaves
+ if (StringUtils.isBlank(props.value(ProcessProperties.CLUSTER_MASTER_HOST))) {
+ commands.add(createWebServerCommand(props, homeDir));
+ commands.add(createCeServerCommand(props, homeDir));
+ }
+
+ return commands;
+ }
+
+ private static JavaCommand createESCommand(Props props, File homeDir) {
JavaCommand elasticsearch = new JavaCommand("search");
elasticsearch
.setWorkDir(homeDir)
.setArguments(props.rawProperties())
.addClasspath("./lib/common/*")
.addClasspath("./lib/search/*");
- commands.add(elasticsearch);
+ return elasticsearch;
+ }
- // do not yet start SQ on elasticsearch slaves
- if (StringUtils.isBlank(props.value(ProcessProperties.CLUSTER_MASTER_HOST))) {
- JavaCommand webServer = new JavaCommand("web")
- .setWorkDir(homeDir)
- .addJavaOptions(ProcessProperties.WEB_ENFORCED_JVM_ARGS)
- .addJavaOptions(props.nonNullValue(ProcessProperties.WEB_JAVA_OPTS))
- .addJavaOptions(props.nonNullValue(ProcessProperties.WEB_JAVA_ADDITIONAL_OPTS))
- // required for logback tomcat valve
- .setEnvVariable(ProcessProperties.PATH_LOGS, props.nonNullValue(ProcessProperties.PATH_LOGS))
- .setClassName("org.sonar.server.app.WebServer")
- .setArguments(props.rawProperties())
- .addClasspath("./lib/common/*")
- .addClasspath("./lib/server/*");
- String driverPath = props.value(ProcessProperties.JDBC_DRIVER_PATH);
- if (driverPath != null) {
- webServer.addClasspath(driverPath);
- }
- commands.add(webServer);
+ private static JavaCommand createWebServerCommand(Props props, File homeDir) {
+ JavaCommand webServer = new JavaCommand("web")
+ .setWorkDir(homeDir)
+ .addJavaOptions(ProcessProperties.WEB_ENFORCED_JVM_ARGS)
+ .addJavaOptions(props.nonNullValue(ProcessProperties.WEB_JAVA_OPTS))
+ .addJavaOptions(props.nonNullValue(ProcessProperties.WEB_JAVA_ADDITIONAL_OPTS))
+ // required for logback tomcat valve
+ .setEnvVariable(ProcessProperties.PATH_LOGS, props.nonNullValue(ProcessProperties.PATH_LOGS))
+ .setClassName("org.sonar.server.app.WebServer")
+ .setArguments(props.rawProperties())
+ .addClasspath("./lib/common/*")
+ .addClasspath("./lib/server/*");
+ String driverPath = props.value(ProcessProperties.JDBC_DRIVER_PATH);
+ if (driverPath != null) {
+ webServer.addClasspath(driverPath);
}
- return commands;
+ return webServer;
+ }
+
+ private static JavaCommand createCeServerCommand(Props props, File homeDir) {
+ JavaCommand webServer = new JavaCommand("ce")
+ .setWorkDir(homeDir)
+ .setClassName("org.sonar.ce.app.CeServer")
+ .setArguments(props.rawProperties())
+ .addClasspath("./lib/common/*")
+ .addClasspath("./lib/server/*")
+ .addClasspath("./lib/ce/*");
+ String driverPath = props.value(ProcessProperties.JDBC_DRIVER_PATH);
+ if (driverPath != null) {
+ webServer.addClasspath(driverPath);
+ }
+ return webServer;
}
static String starPath(File homeDir, String relativePath) {
ArgumentCaptor<List<JavaCommand>> argument = ArgumentCaptor.forClass(listClass);
verify(monitor).start(argument.capture());
- assertThat(argument.getValue()).extracting("key").containsExactly("search", "web");
+ assertThat(argument.getValue()).extracting("key").containsExactly("search", "web", "ce");
}
@Test