--- /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.ce.log.CeLogging;
+import org.sonar.core.platform.Module;
+import org.sonar.process.systeminfo.ProcessStateSystemInfo;
+import org.sonar.server.computation.configuration.CeConfigurationImpl;
+import org.sonar.server.computation.monitoring.CeDatabaseMBeanImpl;
+
+public class CeConfigurationModule extends Module {
+ @Override
+ protected void configureModule() {
+ add(
+ CeConfigurationImpl.class,
+ CeLogging.class,
+ CeDatabaseMBeanImpl.class,
+ new ProcessStateSystemInfo("Compute Engine State"));
+ }
+}
--- /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.ce.httpd.CeHttpServer;
+import org.sonar.ce.systeminfo.SystemInfoHttpAction;
+import org.sonar.core.platform.Module;
+
+public class CeHttpModule extends Module {
+ @Override
+ protected void configureModule() {
+ add(
+ CeHttpServer.class,
+ SystemInfoHttpAction.class);
+ }
+}
+++ /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.ce.log.CeLogging;
-import org.sonar.core.platform.Module;
-import org.sonar.db.purge.period.DefaultPeriodCleaner;
-import org.sonar.process.systeminfo.ProcessStateSystemInfo;
-import org.sonar.process.systeminfo.SystemInfoHttpServer;
-import org.sonar.server.computation.configuration.CeConfigurationImpl;
-import org.sonar.server.computation.dbcleaner.IndexPurgeListener;
-import org.sonar.server.computation.dbcleaner.ProjectCleaner;
-import org.sonar.server.computation.monitoring.CeDatabaseMBeanImpl;
-
-/**
- * Globally available components in CE
- */
-public class CeModule extends Module {
- @Override
- protected void configureModule() {
- add(
- CeConfigurationImpl.class,
- CeLogging.class,
- CeDatabaseMBeanImpl.class,
- SystemInfoHttpServer.class,
- new ProcessStateSystemInfo("Compute Engine State"),
-
- DefaultPeriodCleaner.class,
- ProjectCleaner.class,
- IndexPurgeListener.class);
- }
-}
--- /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.core.platform.Module;
+import org.sonar.db.purge.period.DefaultPeriodCleaner;
+import org.sonar.server.computation.dbcleaner.IndexPurgeListener;
+import org.sonar.server.computation.dbcleaner.ProjectCleaner;
+
+/**
+ * Globally available components in CE for tasks to use.
+ */
+public class CeTaskCommonsModule extends Module {
+ @Override
+ protected void configureModule() {
+ add(
+ DefaultPeriodCleaner.class,
+ ProjectCleaner.class,
+ IndexPurgeListener.class);
+ }
+}
import org.sonar.api.utils.System2;
import org.sonar.api.utils.UriReader;
import org.sonar.api.utils.Version;
+import org.sonar.ce.CeConfigurationModule;
+import org.sonar.ce.CeHttpModule;
+import org.sonar.ce.CeQueueModule;
+import org.sonar.ce.CeTaskCommonsModule;
import org.sonar.ce.db.ReadOnlyPropertiesDao;
import org.sonar.ce.es.EsIndexerEnabler;
import org.sonar.ce.platform.ComputeEngineExtensionInstaller;
import org.sonar.server.component.ComponentCleanerService;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.component.ComponentService;
-import org.sonar.ce.CeModule;
-import org.sonar.ce.CeQueueModule;
import org.sonar.server.computation.task.projectanalysis.ProjectAnalysisTaskModule;
import org.sonar.server.computation.taskprocessor.CeTaskProcessorModule;
import org.sonar.server.debt.DebtModelPluginRepository;
PrivilegedPluginsStopper.class,
// Compute engine (must be after Views and Developer Cockpit)
- CeModule.class,
+ CeConfigurationModule.class,
CeQueueModule.class,
+ CeHttpModule.class,
+ CeTaskCommonsModule.class,
ProjectAnalysisTaskModule.class,
CeTaskProcessorModule.class,
// CeWsModule.class, no Web Service in CE
--- /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.httpd;
+
+import fi.iki.elonen.NanoHTTPD;
+import java.io.File;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Properties;
+import org.slf4j.LoggerFactory;
+import org.sonar.process.DefaultProcessCommands;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static fi.iki.elonen.NanoHTTPD.Response.Status.INTERNAL_ERROR;
+import static fi.iki.elonen.NanoHTTPD.Response.Status.NOT_FOUND;
+import static java.lang.Integer.parseInt;
+import static java.util.Objects.requireNonNull;
+import static org.sonar.process.ProcessEntryPoint.PROPERTY_PROCESS_INDEX;
+import static org.sonar.process.ProcessEntryPoint.PROPERTY_SHARED_PATH;
+
+/**
+ * This HTTP server exports data required for display of System Info page (and the related web service).
+ * It listens on loopback address only, so it does not need to be secure (no HTTPS, no authentication).
+ */
+public class CeHttpServer {
+
+ private final Properties processProps;
+ private final List<HttpAction> actions;
+ private final ActionRegistryImpl actionRegistry;
+ private final CeNanoHttpd nanoHttpd;
+
+ public CeHttpServer(Properties processProps, List<HttpAction> actions) throws UnknownHostException {
+ this.processProps = processProps;
+ this.actions = actions;
+ this.actionRegistry = new ActionRegistryImpl();
+ InetAddress loopbackAddress = InetAddress.getByName(null);
+ this.nanoHttpd = new CeNanoHttpd(loopbackAddress.getHostAddress(), 0, actionRegistry);
+ }
+
+ // do not rename. This naming convention is required for picocontainer.
+ public void start() {
+ try {
+ registerActions();
+ nanoHttpd.start();
+ registerHttpUrl();
+ } catch (IOException e) {
+ throw new IllegalStateException("Can not start local HTTP server for System Info monitoring", e);
+ }
+ }
+
+ private void registerActions() {
+ actions.forEach(action -> action.register(this.actionRegistry));
+ }
+
+ private void registerHttpUrl() {
+ int processNumber = parseInt(processProps.getProperty(PROPERTY_PROCESS_INDEX));
+ File shareDir = new File(processProps.getProperty(PROPERTY_SHARED_PATH));
+ try (DefaultProcessCommands commands = DefaultProcessCommands.secondary(shareDir, processNumber)) {
+ String url = getUrl();
+ commands.setSystemInfoUrl(url);
+ LoggerFactory.getLogger(getClass()).debug("System Info HTTP server listening at {}", url);
+ }
+ }
+
+ // do not rename. This naming convention is required for picocontainer.
+ public void stop() {
+ nanoHttpd.stop();
+ }
+
+ // visible for testing
+ String getUrl() {
+ return "http://" + nanoHttpd.getHostname() + ":" + nanoHttpd.getListeningPort();
+ }
+
+ private class CeNanoHttpd extends NanoHTTPD {
+ private final NanoHTTPD.Response response404 = newFixedLengthResponse(NOT_FOUND, MIME_PLAINTEXT, "Error 404, not found.");
+
+ private final ActionRegistryImpl actionRegistry;
+
+ CeNanoHttpd(String hostname, int port, ActionRegistryImpl actionRegistry) {
+ super(hostname, port);
+ this.actionRegistry = actionRegistry;
+ }
+
+ @Override
+ public Response serve(IHTTPSession session) {
+ return actionRegistry.getAction(session)
+ .map(action -> serveFromAction(session, action))
+ .orElse(response404);
+ }
+
+ private Response serveFromAction(IHTTPSession session, HttpAction action) {
+ try {
+ return action.serve(session);
+ } catch (Exception e) {
+ return newFixedLengthResponse(INTERNAL_ERROR, MIME_PLAINTEXT, e.getMessage());
+ }
+ }
+ }
+
+ private static final class ActionRegistryImpl implements HttpAction.ActionRegistry {
+ private final Map<String, HttpAction> actionsByPath = new HashMap<>();
+
+ @Override
+ public void register(String path, HttpAction action) {
+ requireNonNull(path, "path can't be null");
+ requireNonNull(action, "action can't be null");
+ checkArgument(!path.isEmpty(), "path can't be empty");
+ checkArgument(!path.startsWith("/"), "path must not start with '/'");
+ String fixedPath = path.toLowerCase(Locale.ENGLISH);
+ HttpAction existingAction = actionsByPath.put(fixedPath, action);
+ checkState(existingAction == null, "Action '%s' already registered for path '%s'", existingAction, fixedPath);
+ }
+
+ Optional<HttpAction> getAction(NanoHTTPD.IHTTPSession session) {
+ String path = session.getUri().substring(1).toLowerCase(Locale.ENGLISH);
+ return Optional.ofNullable(actionsByPath.get(path));
+ }
+ }
+}
--- /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.httpd;
+
+import fi.iki.elonen.NanoHTTPD.IHTTPSession;
+import fi.iki.elonen.NanoHTTPD.Response;
+
+/**
+ * a Http action of the CE's HTTP server handles a request for a specified path.
+ *
+ * <p>
+ * Method {@link #register(ActionRegistry)} of the action will be called right before the HTTP server is started (server
+ * is started by the Pico Container). It's the action's responsability to call the method
+ * {@link ActionRegistry#register(ActionRegistry)} to register itself for a given path.
+ * </p>
+ * <p>
+ * Method {@link #serve(IHTTPSession)} will be called each time a request matching the path the action registered itself
+ * for.
+ * </p>
+ */
+public interface HttpAction {
+ void register(ActionRegistry registry);
+
+ Response serve(IHTTPSession session);
+
+ interface ActionRegistry {
+ /**
+ * @throws NullPointerException if {@code path} of {@code action} is {@code null}
+ * @throws IllegalArgumentException if {@code path} is empty or starts with {@code /}
+ * @throws IllegalStateException if an action is already registered for the specified path (case is ignored)
+ */
+ void register(String path, HttpAction action);
+ }
+}
--- /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.httpd;
+
+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.systeminfo;
+
+import fi.iki.elonen.NanoHTTPD;
+import java.io.ByteArrayInputStream;
+import java.util.List;
+import org.sonar.ce.httpd.HttpAction;
+import org.sonar.process.systeminfo.SystemInfoSection;
+import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
+
+import static fi.iki.elonen.NanoHTTPD.newFixedLengthResponse;
+
+public class SystemInfoHttpAction implements HttpAction {
+
+ private static final String PATH = "systemInfo";
+ private static final String PROTOBUF_MIME_TYPE = "application/x-protobuf";
+
+ private final List<SystemInfoSection> sectionProviders;
+
+ public SystemInfoHttpAction(List<SystemInfoSection> sectionProviders) {
+ this.sectionProviders = sectionProviders;
+ }
+
+ @Override
+ public void register(ActionRegistry registry) {
+ registry.register(PATH, this);
+ }
+
+ @Override
+ public NanoHTTPD.Response serve(NanoHTTPD.IHTTPSession session) {
+ ProtobufSystemInfo.SystemInfo.Builder infoBuilder = ProtobufSystemInfo.SystemInfo.newBuilder();
+ for (SystemInfoSection sectionProvider : sectionProviders) {
+ ProtobufSystemInfo.Section section = sectionProvider.toProtobuf();
+ infoBuilder.addSections(section);
+ }
+ byte[] bytes = infoBuilder.build().toByteArray();
+ return newFixedLengthResponse(NanoHTTPD.Response.Status.OK, PROTOBUF_MIME_TYPE, new ByteArrayInputStream(bytes), bytes.length);
+ }
+}
--- /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.systeminfo;
+
+import javax.annotation.ParametersAreNonnullByDefault;
.hasSize(
CONTAINER_ITSELF
+ 75 // level 4
- + 6 // content of CeModule
- + 7 // content of CeQueueModule
- + 3 // content of ProjectAnalysisTaskModule
+ + 7 // content of CeConfigurationModule
+ + 2 // content of CeHttpModule
+ + 5 // content of CeQueueModule
+ + 4 // content of ProjectAnalysisTaskModule
+ 4 // content of CeTaskProcessorModule
);
assertThat(picoContainer.getParent().getComponentAdapters()).hasSize(
--- /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.httpd;
+
+import fi.iki.elonen.NanoHTTPD;
+import java.io.File;
+import java.io.IOException;
+import java.net.ConnectException;
+import java.util.Properties;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import org.apache.commons.io.IOUtils;
+import org.junit.After;
+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.DefaultProcessCommands;
+
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.process.ProcessEntryPoint.PROPERTY_PROCESS_INDEX;
+import static org.sonar.process.ProcessEntryPoint.PROPERTY_SHARED_PATH;
+
+public class CeHttpServerTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private CeHttpServer underTest;
+ private File sharedDir;
+
+ @Before
+ public void setUp() throws Exception {
+ this.sharedDir = temp.newFolder();
+ Properties properties = new Properties();
+ properties.setProperty(PROPERTY_PROCESS_INDEX, "1");
+ properties.setProperty(PROPERTY_SHARED_PATH, sharedDir.getAbsolutePath());
+ underTest = new CeHttpServer(properties, singletonList(new PomPomAction()));
+ underTest.start();
+ }
+
+ @After
+ public void tearDown() {
+ underTest.stop();
+ }
+
+ @Test
+ public void start_publishes_URL_in_IPC() throws Exception {
+ try (DefaultProcessCommands commands = DefaultProcessCommands.secondary(this.sharedDir, 1)) {
+ assertThat(commands.getSystemInfoUrl()).startsWith("http://127.0.0.1:");
+ }
+ }
+
+ @Test
+ public void unkownUrl_return_a_404() throws IOException {
+ assertThat(call(underTest.getUrl() + "/dfdsfdsfsdsd").code()).isEqualTo(404);
+ }
+
+ @Test
+ public void action_is_matched_on_exact_URL() throws IOException {
+ Response response = call(underTest.getUrl() + "/pompom");
+ assertIsPomPomResponse(response);
+ }
+
+ @Test
+ public void action_is_matched_on_URL_ignoring_case() throws IOException {
+ Response response = call(underTest.getUrl() + "/pOMpoM");
+ assertIsPomPomResponse(response);
+ }
+
+ @Test
+ public void action_is_matched_on_URL_with_parameters() throws IOException {
+ Response response = call(underTest.getUrl() + "/pompom?toto=2");
+ assertIsPomPomResponse(response);
+ }
+
+ @Test
+ public void start_starts_http_server_and_publishes_URL_in_IPC() throws Exception {
+ Response response = call(underTest.getUrl() + "/pompom?toto=2");
+ assertIsPomPomResponse(response);
+ }
+
+ private void assertIsPomPomResponse(Response response) throws IOException {
+ assertThat(response.code()).isEqualTo(200);
+ assertThat(IOUtils.toString(response.body().byteStream())).isEqualTo("ok");
+ }
+
+ @Test
+ public void stop_stops_http_server() throws Exception {
+ underTest.stop();
+ expectedException.expect(ConnectException.class);
+ call(underTest.getUrl());
+ }
+
+ private static Response call(String url) throws IOException {
+ Request request = new Request.Builder().get().url(url).build();
+ return new OkHttpClient().newCall(request).execute();
+ }
+
+ private static class PomPomAction implements HttpAction {
+ @Override
+ public void register(ActionRegistry registry) {
+ registry.register("pompom", this);
+ }
+
+ @Override
+ public NanoHTTPD.Response serve(NanoHTTPD.IHTTPSession session) {
+ return NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.OK, NanoHTTPD.MIME_PLAINTEXT, "ok");
+ }
+ }
+}
--- /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.systeminfo;
+
+import fi.iki.elonen.NanoHTTPD;
+import java.util.Arrays;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.ce.httpd.HttpAction;
+import org.sonar.process.systeminfo.ProcessStateSystemInfo;
+import org.sonar.process.systeminfo.SystemInfoSection;
+import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class SystemInfoHttpActionTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private SystemInfoSection stateProvider1 = new ProcessStateSystemInfo("state1");
+ private SystemInfoSection stateProvider2 = new ProcessStateSystemInfo("state2");
+ private SystemInfoHttpAction underTest;
+
+ @Before
+ public void setUp() throws Exception {
+ underTest = new SystemInfoHttpAction(Arrays.asList(stateProvider1, stateProvider2));
+ }
+
+ @Test
+ public void register_to_path_systemInfo() {
+ HttpAction.ActionRegistry actionRegistry = mock(HttpAction.ActionRegistry.class);
+
+ underTest.register(actionRegistry);
+
+ verify(actionRegistry).register("systemInfo", underTest);
+ }
+
+ @Test
+ public void start_starts_http_server_and_publishes_URL_in_IPC() throws Exception {
+ NanoHTTPD.Response response = underTest.serve(mock(NanoHTTPD.IHTTPSession.class));
+ assertThat(response.getStatus()).isEqualTo(NanoHTTPD.Response.Status.OK);
+ ProtobufSystemInfo.SystemInfo systemInfo = ProtobufSystemInfo.SystemInfo.parseFrom(response.getData());
+ assertThat(systemInfo.getSectionsCount()).isEqualTo(2);
+ assertThat(systemInfo.getSections(0).getName()).isEqualTo("state1");
+ assertThat(systemInfo.getSections(1).getName()).isEqualTo("state2");
+ }
+
+}
+++ /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.systeminfo;
-
-import fi.iki.elonen.NanoHTTPD;
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.List;
-import java.util.Properties;
-import org.slf4j.LoggerFactory;
-import org.sonar.process.DefaultProcessCommands;
-import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
-
-import static java.lang.Integer.parseInt;
-import static org.sonar.process.ProcessEntryPoint.PROPERTY_PROCESS_INDEX;
-import static org.sonar.process.ProcessEntryPoint.PROPERTY_SHARED_PATH;
-
-/**
- * This HTTP server exports data required for display of System Info page (and the related web service).
- * It listens on loopback address only, so it does not need to be secure (no HTTPS, no authentication).
- */
-public class SystemInfoHttpServer {
-
- private static final String PROTOBUF_MIME_TYPE = "application/x-protobuf";
-
- private final Properties processProps;
- private final List<SystemInfoSection> sectionProviders;
- private final SystemInfoNanoHttpd nanoHttpd;
-
- public SystemInfoHttpServer(Properties processProps, List<SystemInfoSection> sectionProviders) throws UnknownHostException {
- this.processProps = processProps;
- this.sectionProviders = sectionProviders;
- InetAddress loopbackAddress = InetAddress.getByName(null);
- this.nanoHttpd = new SystemInfoNanoHttpd(loopbackAddress.getHostAddress(), 0);
- }
-
- // do not rename. This naming convention is required for picocontainer.
- public void start() {
- try {
- nanoHttpd.start();
- registerHttpUrl();
- } catch (IOException e) {
- throw new IllegalStateException("Can not start local HTTP server for System Info monitoring", e);
- }
- }
-
- private void registerHttpUrl() {
- int processNumber = parseInt(processProps.getProperty(PROPERTY_PROCESS_INDEX));
- File shareDir = new File(processProps.getProperty(PROPERTY_SHARED_PATH));
- try (DefaultProcessCommands commands = DefaultProcessCommands.secondary(shareDir, processNumber)) {
- String url = getUrl();
- commands.setSystemInfoUrl(url);
- LoggerFactory.getLogger(getClass()).debug("System Info HTTP server listening at {}", url);
- }
- }
-
- // do not rename. This naming convention is required for picocontainer.
- public void stop() {
- nanoHttpd.stop();
- }
-
- // visible for testing
- String getUrl() {
- return "http://" + nanoHttpd.getHostname() + ":" + nanoHttpd.getListeningPort();
- }
-
- private class SystemInfoNanoHttpd extends NanoHTTPD {
-
- SystemInfoNanoHttpd(String hostname, int port) {
- super(hostname, port);
- }
-
- @Override
- public Response serve(IHTTPSession session) {
- ProtobufSystemInfo.SystemInfo.Builder infoBuilder = ProtobufSystemInfo.SystemInfo.newBuilder();
- for (SystemInfoSection sectionProvider : sectionProviders) {
- ProtobufSystemInfo.Section section = sectionProvider.toProtobuf();
- infoBuilder.addSections(section);
- }
- byte[] bytes = infoBuilder.build().toByteArray();
- return newFixedLengthResponse(Response.Status.OK, PROTOBUF_MIME_TYPE, new ByteArrayInputStream(bytes), bytes.length);
- }
- }
-}
+++ /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.systeminfo;
-
-import okhttp3.OkHttpClient;
-import okhttp3.Request;
-import okhttp3.Response;
-import java.io.IOException;
-import java.net.ConnectException;
-import java.util.Arrays;
-import java.util.Properties;
-import org.junit.After;
-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.systeminfo.protobuf.ProtobufSystemInfo;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonar.process.ProcessEntryPoint.PROPERTY_PROCESS_INDEX;
-import static org.sonar.process.ProcessEntryPoint.PROPERTY_SHARED_PATH;
-
-public class SystemInfoHttpServerTest {
-
- @Rule
- public TemporaryFolder temp = new TemporaryFolder();
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- SystemInfoSection stateProvider1 = new ProcessStateSystemInfo("state1");
- SystemInfoSection stateProvider2 = new ProcessStateSystemInfo("state2");
- SystemInfoHttpServer underTest;
-
- @Before
- public void setUp() throws Exception {
- Properties properties = new Properties();
- properties.setProperty(PROPERTY_PROCESS_INDEX, "1");
- properties.setProperty(PROPERTY_SHARED_PATH, temp.newFolder().getAbsolutePath());
- underTest = new SystemInfoHttpServer(properties, Arrays.asList(stateProvider1, stateProvider2));
- }
-
- @After
- public void tearDown() {
- underTest.stop();
- }
-
- @Test
- public void start_starts_http_server_and_publishes_URL_in_IPC() throws Exception {
- underTest.start();
- Response response = call(underTest.getUrl());
- assertThat(response.code()).isEqualTo(200);
- ProtobufSystemInfo.SystemInfo systemInfo = ProtobufSystemInfo.SystemInfo.parseFrom(response.body().bytes());
- assertThat(systemInfo.getSectionsCount()).isEqualTo(2);
- assertThat(systemInfo.getSections(0).getName()).isEqualTo("state1");
- assertThat(systemInfo.getSections(1).getName()).isEqualTo("state2");
- }
-
- @Test
- public void stop_stops_http_server() throws Exception {
- underTest.start();
- underTest.stop();
- expectedException.expect(ConnectException.class);
- call(underTest.getUrl());
- }
-
- private static Response call(String url) throws IOException {
- Request request = new Request.Builder().get().url(url).build();
- return new OkHttpClient().newCall(request).execute();
- }
-}
public Optional<ProtobufSystemInfo.SystemInfo> connect(ProcessId processId) {
try (DefaultProcessCommands commands = DefaultProcessCommands.secondary(ipcSharedDir, processId.getIpcIndex())) {
if (commands.isUp()) {
- String url = commands.getSystemInfoUrl();
+ String url = commands.getSystemInfoUrl() + "/systemInfo";
byte[] protobuf = IOUtils.toByteArray(new URI(url));
return Optional.of(ProtobufSystemInfo.SystemInfo.parseFrom(protobuf));
}