]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7825 alter CE HTTP server for general use
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Fri, 19 Aug 2016 15:46:01 +0000 (17:46 +0200)
committerSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Tue, 23 Aug 2016 13:14:18 +0000 (15:14 +0200)
16 files changed:
server/sonar-ce/src/main/java/org/sonar/ce/CeConfigurationModule.java [new file with mode: 0644]
server/sonar-ce/src/main/java/org/sonar/ce/CeHttpModule.java [new file with mode: 0644]
server/sonar-ce/src/main/java/org/sonar/ce/CeModule.java [deleted file]
server/sonar-ce/src/main/java/org/sonar/ce/CeTaskCommonsModule.java [new file with mode: 0644]
server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java
server/sonar-ce/src/main/java/org/sonar/ce/httpd/CeHttpServer.java [new file with mode: 0644]
server/sonar-ce/src/main/java/org/sonar/ce/httpd/HttpAction.java [new file with mode: 0644]
server/sonar-ce/src/main/java/org/sonar/ce/httpd/package-info.java [new file with mode: 0644]
server/sonar-ce/src/main/java/org/sonar/ce/systeminfo/SystemInfoHttpAction.java [new file with mode: 0644]
server/sonar-ce/src/main/java/org/sonar/ce/systeminfo/package-info.java [new file with mode: 0644]
server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java
server/sonar-ce/src/test/java/org/sonar/ce/httpd/CeHttpServerTest.java [new file with mode: 0644]
server/sonar-ce/src/test/java/org/sonar/ce/systeminfo/SystemInfoHttpActionTest.java [new file with mode: 0644]
server/sonar-process/src/main/java/org/sonar/process/systeminfo/SystemInfoHttpServer.java [deleted file]
server/sonar-process/src/test/java/org/sonar/process/systeminfo/SystemInfoHttpServerTest.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/ProcessSystemInfoClient.java

diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/CeConfigurationModule.java b/server/sonar-ce/src/main/java/org/sonar/ce/CeConfigurationModule.java
new file mode 100644 (file)
index 0000000..7d4f81b
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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"));
+  }
+}
diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/CeHttpModule.java b/server/sonar-ce/src/main/java/org/sonar/ce/CeHttpModule.java
new file mode 100644 (file)
index 0000000..69afca0
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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);
+  }
+}
diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/CeModule.java b/server/sonar-ce/src/main/java/org/sonar/ce/CeModule.java
deleted file mode 100644 (file)
index 0379784..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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);
-  }
-}
diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/CeTaskCommonsModule.java b/server/sonar-ce/src/main/java/org/sonar/ce/CeTaskCommonsModule.java
new file mode 100644 (file)
index 0000000..1f062b8
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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);
+  }
+}
index 1acda5bf67a0fb25c85d6d9890fe30687b40bde1..7d6524dcf489eafb5ffd7271f6e54f20131a9a25 100644 (file)
@@ -39,6 +39,10 @@ import org.sonar.api.utils.Durations;
 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;
@@ -69,8 +73,6 @@ import org.sonar.server.activity.index.ActivityIndexer;
 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;
@@ -622,8 +624,10 @@ public class ComputeEngineContainerImpl implements ComputeEngineContainer {
       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
diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/httpd/CeHttpServer.java b/server/sonar-ce/src/main/java/org/sonar/ce/httpd/CeHttpServer.java
new file mode 100644 (file)
index 0000000..2a90c81
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * 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));
+    }
+  }
+}
diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/httpd/HttpAction.java b/server/sonar-ce/src/main/java/org/sonar/ce/httpd/HttpAction.java
new file mode 100644 (file)
index 0000000..c4d6332
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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);
+  }
+}
diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/httpd/package-info.java b/server/sonar-ce/src/main/java/org/sonar/ce/httpd/package-info.java
new file mode 100644 (file)
index 0000000..43a8387
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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;
diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/systeminfo/SystemInfoHttpAction.java b/server/sonar-ce/src/main/java/org/sonar/ce/systeminfo/SystemInfoHttpAction.java
new file mode 100644 (file)
index 0000000..59bba1c
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * 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);
+  }
+}
diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/systeminfo/package-info.java b/server/sonar-ce/src/main/java/org/sonar/ce/systeminfo/package-info.java
new file mode 100644 (file)
index 0000000..1cd7a4e
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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;
index 22db63201cedf8061cfaf3305a07d07fd39c635a..e220ecfb360b82763b7efcd97a65a8faa29caf83 100644 (file)
@@ -89,9 +89,10 @@ public class ComputeEngineContainerImplTest {
       .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(
diff --git a/server/sonar-ce/src/test/java/org/sonar/ce/httpd/CeHttpServerTest.java b/server/sonar-ce/src/test/java/org/sonar/ce/httpd/CeHttpServerTest.java
new file mode 100644 (file)
index 0000000..2723279
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * 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");
+    }
+  }
+}
diff --git a/server/sonar-ce/src/test/java/org/sonar/ce/systeminfo/SystemInfoHttpActionTest.java b/server/sonar-ce/src/test/java/org/sonar/ce/systeminfo/SystemInfoHttpActionTest.java
new file mode 100644 (file)
index 0000000..cf0d5b3
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * 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");
+  }
+
+}
diff --git a/server/sonar-process/src/main/java/org/sonar/process/systeminfo/SystemInfoHttpServer.java b/server/sonar-process/src/main/java/org/sonar/process/systeminfo/SystemInfoHttpServer.java
deleted file mode 100644 (file)
index b8c08f3..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * 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);
-    }
-  }
-}
diff --git a/server/sonar-process/src/test/java/org/sonar/process/systeminfo/SystemInfoHttpServerTest.java b/server/sonar-process/src/test/java/org/sonar/process/systeminfo/SystemInfoHttpServerTest.java
deleted file mode 100644 (file)
index 04df2a6..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * 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();
-  }
-}
index 3968a2792a0659663dc84ea4df91edcd20e4430e..9e319f286514f1b815976bcef9964df44004c178 100644 (file)
@@ -49,7 +49,7 @@ public class ProcessSystemInfoClient {
   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));
       }