]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7899 Refactor server file system
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Tue, 26 Jul 2016 12:14:33 +0000 (14:14 +0200)
committerSimon Brandhof <simon.brandhof@sonarsource.com>
Fri, 29 Jul 2016 08:31:31 +0000 (10:31 +0200)
- new interface org.sonar.server.platform.ServerFileSystem
- deprecate org.sonar.api.platform.Server#getDeployDir()

70 files changed:
server/sonar-ce/src/main/java/org/sonar/ce/container/CePluginJarExploder.java
server/sonar-ce/src/main/java/org/sonar/ce/container/CePluginRepository.java
server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java
server/sonar-ce/src/test/java/org/sonar/ce/container/CePluginJarExploderTest.java
server/sonar-ce/src/test/java/org/sonar/ce/container/CePluginRepositoryTest.java
server/sonar-server/src/main/java/org/sonar/server/batch/BatchIndex.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/filesystem/ComputationTempFolderProvider.java
server/sonar-server/src/main/java/org/sonar/server/platform/ClassLoaderUtils.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/platform/DefaultServerFileSystem.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/platform/MasterServletFilter.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/platform/PlatformServletContextListener.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/platform/ProfilingFilter.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/platform/RailsAppsDeployer.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/platform/RoutesFilter.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/platform/RubyRailsContextListener.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/platform/SecurityServletFilter.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/platform/ServerFileSystem.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/platform/ServerFileSystemImpl.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/platform/ServerImpl.java
server/sonar-server/src/main/java/org/sonar/server/platform/TempFolderProvider.java
server/sonar-server/src/main/java/org/sonar/server/platform/cluster/ClusterProperties.java
server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel1.java
server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel2.java
server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelStartup.java
server/sonar-server/src/main/java/org/sonar/server/platform/web/MasterServletFilter.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/platform/web/PlatformServletContextListener.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/platform/web/ProfilingFilter.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/platform/web/RailsAppsDeployer.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/platform/web/RegisterServletFilters.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/platform/web/RoutesFilter.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/platform/web/RubyRailsContextListener.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/platform/web/SecurityServletFilter.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/platform/web/package-info.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/plugins/PluginDownloader.java
server/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginJarExploder.java
server/sonar-server/src/main/java/org/sonar/server/plugins/ServerPluginRepository.java
server/sonar-server/src/main/java/org/sonar/server/startup/GeneratePluginIndex.java
server/sonar-server/src/main/java/org/sonar/server/startup/RegisterServletFilters.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/updatecenter/ws/UploadAction.java
server/sonar-server/src/main/java/org/sonar/server/util/ClassLoaderUtils.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/batch/BatchIndexTest.java
server/sonar-server/src/test/java/org/sonar/server/batch/IssuesActionTest.java
server/sonar-server/src/test/java/org/sonar/server/batch/UsersActionTest.java
server/sonar-server/src/test/java/org/sonar/server/platform/ClassLoaderUtilsTest.java
server/sonar-server/src/test/java/org/sonar/server/platform/MasterServletFilterTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/platform/ProfilingFilterTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/platform/RailsAppsDeployerTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/platform/RoutesFilterTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/platform/RubyRailsContextListenerTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/platform/SecurityServletFilterTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/platform/TempFolderProviderTest.java
server/sonar-server/src/test/java/org/sonar/server/platform/cluster/ClusterImplTest.java
server/sonar-server/src/test/java/org/sonar/server/platform/cluster/ClusterMock.java
server/sonar-server/src/test/java/org/sonar/server/platform/web/MasterServletFilterTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/platform/web/ProfilingFilterTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/platform/web/RailsAppsDeployerTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/platform/web/RoutesFilterTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/platform/web/RubyRailsContextListenerTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/platform/web/SecurityServletFilterTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/plugins/PluginDownloaderTest.java
server/sonar-server/src/test/java/org/sonar/server/plugins/ServerPluginJarExploderTest.java
server/sonar-server/src/test/java/org/sonar/server/plugins/ServerPluginRepositoryTest.java
server/sonar-server/src/test/java/org/sonar/server/startup/GeneratePluginIndexTest.java
server/sonar-server/src/test/java/org/sonar/server/startup/RegisterServletFiltersTest.java
server/sonar-server/src/test/java/org/sonar/server/updatecenter/ws/UpdateCenterWsTest.java
server/sonar-server/src/test/java/org/sonar/server/updatecenter/ws/UploadActionTest.java
server/sonar-server/src/test/resources/org/sonar/server/platform/RailsAppsDeployerTest/FakeRubyRailsApp.jar [deleted file]
server/sonar-server/src/test/resources/org/sonar/server/platform/web/RailsAppsDeployerTest/FakeRubyRailsApp.jar [new file with mode: 0644]
server/sonar-web/src/main/webapp/WEB-INF/web.xml
sonar-plugin-api/src/main/java/org/sonar/api/platform/ServerFileSystem.java

index a9d64dd3c374c67ac14219418374bb408264fc85..67587fbd76062cd037d8cb9408161c416771c1c9 100644 (file)
@@ -21,7 +21,7 @@ package org.sonar.ce.container;
 
 import java.io.File;
 import org.apache.commons.io.FileUtils;
-import org.sonar.api.platform.ServerFileSystem;
+import org.sonar.server.platform.ServerFileSystem;
 import org.sonar.api.utils.ZipUtils;
 import org.sonar.core.platform.ExplodedPlugin;
 import org.sonar.core.platform.PluginInfo;
index 0f24f3d6b1d8717f6d37c76ffbb056e3a36a6cdd..540f93f2fe8db16e316c51d74ca97e53c52aaaf9 100644 (file)
@@ -33,7 +33,7 @@ import org.sonar.api.utils.log.Loggers;
 import org.sonar.core.platform.PluginInfo;
 import org.sonar.core.platform.PluginLoader;
 import org.sonar.core.platform.PluginRepository;
-import org.sonar.server.platform.DefaultServerFileSystem;
+import org.sonar.server.platform.ServerFileSystem;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkState;
@@ -48,7 +48,7 @@ public class CePluginRepository implements PluginRepository, Startable {
   private static final String[] JAR_FILE_EXTENSIONS = new String[] {"jar"};
   private static final String NOT_STARTED_YET = "not started yet";
 
-  private final DefaultServerFileSystem fs;
+  private final ServerFileSystem fs;
   private final PluginLoader loader;
   private final AtomicBoolean started = new AtomicBoolean(false);
 
@@ -56,7 +56,7 @@ public class CePluginRepository implements PluginRepository, Startable {
   private final Map<String, PluginInfo> pluginInfosByKeys = new HashMap<>();
   private final Map<String, Plugin> pluginInstancesByKeys = new HashMap<>();
 
-  public CePluginRepository(DefaultServerFileSystem fs, PluginLoader loader) {
+  public CePluginRepository(ServerFileSystem fs, PluginLoader loader) {
     this.fs = fs;
     this.loader = loader;
   }
index b3d4233e5c9c23868751b4f3bdde8107797dd5a6..bab0a7282d178b3d655739bd870c895fff50e294 100644 (file)
@@ -70,9 +70,9 @@ import org.sonar.server.component.ComponentCleanerService;
 import org.sonar.server.component.ComponentFinder;
 import org.sonar.server.component.ComponentService;
 import org.sonar.server.computation.CeModule;
-import org.sonar.server.computation.task.projectanalysis.ProjectAnalysisTaskModule;
 import org.sonar.server.computation.CeQueueModule;
 import org.sonar.server.computation.queue.PurgeCeActivities;
+import org.sonar.server.computation.task.projectanalysis.ProjectAnalysisTaskModule;
 import org.sonar.server.computation.taskprocessor.CeTaskProcessorModule;
 import org.sonar.server.debt.DebtModelPluginRepository;
 import org.sonar.server.debt.DebtRulesXMLImporter;
@@ -99,9 +99,9 @@ import org.sonar.server.notification.NotificationService;
 import org.sonar.server.notification.email.AlertsEmailTemplate;
 import org.sonar.server.notification.email.EmailNotificationChannel;
 import org.sonar.server.platform.DatabaseServerCompatibility;
-import org.sonar.server.platform.DefaultServerFileSystem;
 import org.sonar.server.platform.DefaultServerUpgradeStatus;
 import org.sonar.server.platform.PersistentSettings;
+import org.sonar.server.platform.ServerFileSystemImpl;
 import org.sonar.server.platform.ServerImpl;
 import org.sonar.server.platform.ServerLifecycleNotifier;
 import org.sonar.server.platform.ServerLogging;
@@ -216,7 +216,7 @@ public class ComputeEngineContainerImpl implements ComputeEngineContainer {
       DatabaseServerCompatibility.class,
       DatabaseVersion.class,
       PurgeProfiler.class,
-      DefaultServerFileSystem.class,
+      ServerFileSystemImpl.class,
       // no TempFolderCleaner.class, responsibility of Web Server
       new TempFolderProvider(),
       System2.INSTANCE,
index 4062a8747f30a3665fb4ec470404e686fdd1bdc9..59be2e53d21009e35e8cb723bde416c22eb603a9 100644 (file)
@@ -24,7 +24,7 @@ import java.io.IOException;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
-import org.sonar.api.platform.ServerFileSystem;
+import org.sonar.server.platform.ServerFileSystem;
 import org.sonar.core.platform.ExplodedPlugin;
 import org.sonar.core.platform.PluginInfo;
 
@@ -97,6 +97,16 @@ public class CePluginJarExploderTest {
       this.temp = temp;
     }
 
+    @Override
+    public File getDataDir() {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public File getDeployDir() {
+      throw new UnsupportedOperationException();
+    }
+
     @Override
     public File getHomeDir() {
       throw new UnsupportedOperationException();
@@ -114,5 +124,30 @@ public class CePluginJarExploderTest {
       return tempDir;
     }
 
+    @Override
+    public File getDeployedPluginsDir() {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public File getDownloadedPluginsDir() {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public File getInstalledPluginsDir() {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public File getBundledPluginsDir() {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public File getPluginIndex() {
+      throw new UnsupportedOperationException();
+    }
+
   }
 }
index a7ebbc8eff3728bf0180a868ca140aeedb2aedf2..09c044809bf537f0192cce3374450dac0b402e0b 100644 (file)
@@ -32,7 +32,7 @@ import org.mockito.Mockito;
 import org.sonar.api.Plugin;
 import org.sonar.core.platform.PluginInfo;
 import org.sonar.core.platform.PluginLoader;
-import org.sonar.server.platform.DefaultServerFileSystem;
+import org.sonar.server.platform.ServerFileSystem;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
@@ -46,9 +46,9 @@ public class CePluginRepositoryTest {
   @Rule
   public ExpectedException expectedException = ExpectedException.none();
 
-  DefaultServerFileSystem fs = mock(DefaultServerFileSystem.class, Mockito.RETURNS_DEEP_STUBS);
-  PluginLoader pluginLoader = new DumbPluginLoader();
-  CePluginRepository underTest = new CePluginRepository(fs, pluginLoader);
+  private ServerFileSystem fs = mock(ServerFileSystem.class, Mockito.RETURNS_DEEP_STUBS);
+  private PluginLoader pluginLoader = new DumbPluginLoader();
+  private CePluginRepository underTest = new CePluginRepository(fs, pluginLoader);
 
   @After
   public void tearDown() {
index 451ab800945ebeb06996e4d3e198fadf39da76e7..511c90baf4032e1693cb06960507c03901a3f637 100644 (file)
@@ -30,8 +30,8 @@ import org.apache.commons.io.filefilter.HiddenFileFilter;
 import org.apache.commons.lang.CharUtils;
 import org.apache.commons.lang.StringUtils;
 import org.picocontainer.Startable;
-import org.sonar.api.platform.Server;
 import org.sonar.api.server.ServerSide;
+import org.sonar.server.platform.ServerFileSystem;
 
 /**
  * JAR files to be downloaded by sonar-runner.
@@ -39,18 +39,18 @@ import org.sonar.api.server.ServerSide;
 @ServerSide
 public class BatchIndex implements Startable {
 
-  private final Server server;
+  private final ServerFileSystem fs;
   private String index;
   private File batchDir;
 
-  public BatchIndex(Server server) {
-    this.server = server;
+  public BatchIndex(ServerFileSystem fs) {
+    this.fs = fs;
   }
 
   @Override
   public void start() {
     StringBuilder sb = new StringBuilder();
-    batchDir = new File(server.getRootDir(), "lib/batch");
+    batchDir = new File(fs.getHomeDir(), "lib/batch");
     if (batchDir.exists()) {
       Collection<File> files = FileUtils.listFiles(batchDir, HiddenFileFilter.VISIBLE, FileFilterUtils.directoryFileFilter());
       for (File file : files) {
index c4425c0b43ae72add4a3aa9176c607d6da7640aa..f810ee15b03617d4a9238cc4dcd67cc5a513708b 100644 (file)
@@ -26,9 +26,9 @@ import org.apache.commons.io.FileUtils;
 import org.picocontainer.ComponentLifecycle;
 import org.picocontainer.PicoContainer;
 import org.picocontainer.injectors.ProviderAdapter;
-import org.sonar.api.platform.ServerFileSystem;
 import org.sonar.api.utils.TempFolder;
 import org.sonar.api.utils.internal.DefaultTempFolder;
+import org.sonar.server.platform.ServerFileSystem;
 
 /**
  * Provides a TempFolder instance pointing to a directory dedicated to the processing of a specific item.
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ClassLoaderUtils.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ClassLoaderUtils.java
deleted file mode 100644 (file)
index 4a067f9..0000000
+++ /dev/null
@@ -1,141 +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.server.platform;
-
-import com.google.common.base.Function;
-import com.google.common.base.Predicate;
-import com.google.common.base.Throwables;
-import com.google.common.collect.Lists;
-import java.io.File;
-import java.io.IOException;
-import java.net.URL;
-import java.net.URLDecoder;
-import java.util.Collection;
-import java.util.Enumeration;
-import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
-import javax.annotation.Nullable;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.lang.CharEncoding;
-import org.apache.commons.lang.StringUtils;
-import org.sonar.api.utils.log.Loggers;
-
-/**
- * @since 3.0
- */
-class ClassLoaderUtils {
-
-  private ClassLoaderUtils() {
-    // only static methods
-  }
-
-  static File copyResources(ClassLoader classLoader, String rootPath, File toDir, Function<String, String> relocationFunction) {
-    Collection<String> relativePaths = listFiles(classLoader, rootPath);
-    for (String relativePath : relativePaths) {
-      URL resource = classLoader.getResource(relativePath);
-      String filename = relocationFunction.apply(relativePath);
-      File toFile = new File(toDir, filename);
-      try {
-        FileUtils.copyURLToFile(resource, toFile);
-      } catch (IOException e) {
-        throw new IllegalStateException("Fail to extract " + relativePath + " to " + toFile.getAbsolutePath(), e);
-      }
-    }
-
-    return toDir;
-  }
-
-  /**
-   * Finds files within a given directory and its subdirectories
-   *
-   * @param classLoader
-   * @param rootPath    the root directory, for example org/sonar/sqale
-   * @return a list of relative paths, for example {"org/sonar/sqale/foo/bar.txt}. Never null.
-   */
-  static Collection<String> listFiles(ClassLoader classLoader, String rootPath) {
-    return listResources(classLoader, rootPath, new Predicate<String>() {
-      @Override
-      public boolean apply(@Nullable String path) {
-        return !StringUtils.endsWith(path, "/");
-      }
-    });
-  }
-
-  /**
-   * Finds directories and files within a given directory and its subdirectories.
-   *
-   * @param classLoader
-   * @param rootPath    the root directory, for example org/sonar/sqale, or a file in this root directory, for example org/sonar/sqale/index.txt
-   * @param predicate
-   * @return a list of relative paths, for example {"org/sonar/sqale", "org/sonar/sqale/foo", "org/sonar/sqale/foo/bar.txt}. Never null.
-   */
-  static Collection<String> listResources(ClassLoader classLoader, String rootPath, Predicate<String> predicate) {
-    String jarPath = null;
-    JarFile jar = null;
-    try {
-      Collection<String> paths = Lists.newArrayList();
-      URL root = classLoader.getResource(rootPath);
-      if (root != null) {
-        checkJarFile(root);
-
-        // Path of the root directory
-        // Examples :
-        // org/sonar/sqale/index.txt  -> rootDirectory is org/sonar/sqale
-        // org/sonar/sqale/  -> rootDirectory is org/sonar/sqale
-        // org/sonar/sqale  -> rootDirectory is org/sonar/sqale
-        String rootDirectory = rootPath;
-        if (StringUtils.substringAfterLast(rootPath, "/").indexOf('.') >= 0) {
-          rootDirectory = StringUtils.substringBeforeLast(rootPath, "/");
-        }
-        //strip out only the JAR file
-        jarPath = root.getPath().substring(5, root.getPath().indexOf('!'));
-        jar = new JarFile(URLDecoder.decode(jarPath, CharEncoding.UTF_8));
-        Enumeration<JarEntry> entries = jar.entries();
-        while (entries.hasMoreElements()) {
-          String name = entries.nextElement().getName();
-          if (name.startsWith(rootDirectory) && predicate.apply(name)) {
-            paths.add(name);
-          }
-        }
-      }
-      return paths;
-    } catch (Exception e) {
-      throw Throwables.propagate(e);
-    } finally {
-      closeJar(jar, jarPath);
-    }
-  }
-
-  private static void closeJar(JarFile jar, String jarPath) {
-    if (jar != null) {
-      try {
-        jar.close();
-      } catch (Exception e) {
-        Loggers.get(ClassLoaderUtils.class).error("Fail to close JAR file: " + jarPath, e);
-      }
-    }
-  }
-
-  private static void checkJarFile(URL root) {
-    if (!"jar".equals(root.getProtocol())) {
-      throw new IllegalStateException("Unsupported protocol: " + root.getProtocol());
-    }
-  }
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/DefaultServerFileSystem.java b/server/sonar-server/src/main/java/org/sonar/server/platform/DefaultServerFileSystem.java
deleted file mode 100644 (file)
index 3afca7f..0000000
+++ /dev/null
@@ -1,124 +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.server.platform;
-
-import java.io.File;
-import java.io.IOException;
-import javax.annotation.CheckForNull;
-import org.apache.commons.io.FileUtils;
-import org.picocontainer.Startable;
-import org.sonar.api.config.Settings;
-import org.sonar.api.platform.Server;
-import org.sonar.api.platform.ServerFileSystem;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.process.ProcessProperties;
-
-/**
- * Introspect the filesystem and the classloader to get extension files at startup.
- *
- * @since 2.2
- */
-public class DefaultServerFileSystem implements ServerFileSystem, Startable {
-
-  private static final Logger LOGGER = Loggers.get(DefaultServerFileSystem.class);
-
-  private final Server server;
-  private final File homeDir;
-  private final File tempDir;
-
-  public DefaultServerFileSystem(Settings settings, Server server) {
-    this.server = server;
-    this.homeDir = new File(settings.getString(ProcessProperties.PATH_HOME));
-    this.tempDir = new File(settings.getString(ProcessProperties.PATH_TEMP));
-  }
-
-  /**
-   * for unit tests
-   */
-  public DefaultServerFileSystem(File homeDir, File tempDir, Server server) {
-    this.homeDir = homeDir;
-    this.tempDir = tempDir;
-    this.server = server;
-  }
-
-  @Override
-  public void start() {
-    LOGGER.info("SonarQube home: " + homeDir.getAbsolutePath());
-
-    File deployDir = getDeployDir();
-    if (deployDir == null) {
-      throw new IllegalArgumentException("Web app directory does not exist");
-    }
-
-    File deprecated = getDeprecatedPluginsDir();
-    try {
-      FileUtils.forceMkdir(deprecated);
-      org.sonar.core.util.FileUtils.cleanDirectory(deprecated);
-    } catch (IOException e) {
-      throw new IllegalStateException("The following directory can not be created: " + deprecated.getAbsolutePath(), e);
-    }
-  }
-
-  @Override
-  public void stop() {
-    // do nothing
-  }
-
-  @Override
-  public File getHomeDir() {
-    return homeDir;
-  }
-
-  @Override
-  public File getTempDir() {
-    return tempDir;
-  }
-
-  @CheckForNull
-  public File getDeployDir() {
-    return server.getDeployDir();
-  }
-
-  public File getDeployedPluginsDir() {
-    return new File(getDeployDir(), "plugins");
-  }
-
-  public File getDownloadedPluginsDir() {
-    return new File(getHomeDir(), "extensions/downloads");
-  }
-
-  public File getInstalledPluginsDir() {
-    return new File(getHomeDir(), "extensions/plugins");
-  }
-
-  public File getBundledPluginsDir() {
-    return new File(getHomeDir(), "lib/bundled-plugins");
-  }
-
-  public File getDeprecatedPluginsDir() {
-    return new File(getHomeDir(), "extensions/deprecated");
-  }
-
-  public File getPluginIndex() {
-    return new File(getDeployDir(), "plugins/index.txt");
-  }
-
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/MasterServletFilter.java b/server/sonar-server/src/main/java/org/sonar/server/platform/MasterServletFilter.java
deleted file mode 100644 (file)
index 9985abc..0000000
+++ /dev/null
@@ -1,136 +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.server.platform;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Lists;
-import java.io.IOException;
-import java.util.Iterator;
-import java.util.List;
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.api.web.ServletFilter;
-
-/**
- * Inspired by http://stackoverflow.com/a/7592883/229031
- */
-public class MasterServletFilter implements Filter {
-
-  public static volatile MasterServletFilter INSTANCE;
-  private ServletFilter[] filters;
-  private FilterConfig config;
-
-  public MasterServletFilter() {
-    if (INSTANCE != null) {
-      throw new IllegalStateException("Servlet filter " + getClass().getName() + " is already instantiated");
-    }
-    INSTANCE = this;
-  }
-
-  @Override
-  public void init(FilterConfig config) throws ServletException {
-    // Filters are already available in picocontainer unless a database migration is required. See
-    // org.sonar.server.startup.RegisterServletFilters.
-    init(config, Platform.getInstance().getContainer().getComponentsByType(ServletFilter.class));
-  }
-
-  void init(FilterConfig config, List<ServletFilter> filters) throws ServletException {
-    this.config = config;
-    initFilters(filters);
-  }
-
-  public void initFilters(List<ServletFilter> filterExtensions) throws ServletException {
-    List<ServletFilter> filterList = Lists.newArrayList();
-    for (ServletFilter extension : filterExtensions) {
-      try {
-        Loggers.get(MasterServletFilter.class).info(String.format("Initializing servlet filter %s [pattern=%s]", extension, extension.doGetPattern()));
-        extension.init(config);
-        filterList.add(extension);
-      } catch (Exception e) {
-        throw new IllegalStateException("Fail to initialize servlet filter: " + extension + ". Message: " + e.getMessage(), e);
-      }
-    }
-    filters = filterList.toArray(new ServletFilter[filterList.size()]);
-  }
-
-  @Override
-  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
-    HttpServletRequest hsr = (HttpServletRequest) request;
-    if (filters.length == 0) {
-      chain.doFilter(request, response);
-    } else {
-      String path = hsr.getRequestURI().replaceFirst(hsr.getContextPath(), "");
-      GodFilterChain godChain = new GodFilterChain(chain);
-
-      for (ServletFilter filter : filters) {
-        if (filter.doGetPattern().matches(path)) {
-          godChain.addFilter(filter);
-        }
-      }
-      godChain.doFilter(request, response);
-    }
-  }
-
-  @Override
-  public void destroy() {
-    for (ServletFilter filter : filters) {
-      filter.destroy();
-    }
-  }
-
-  @VisibleForTesting
-  ServletFilter[] getFilters() {
-    return filters;
-  }
-
-  private static final class GodFilterChain implements FilterChain {
-    private FilterChain chain;
-    private List<Filter> filters = Lists.newLinkedList();
-    private Iterator<Filter> iterator;
-
-    public GodFilterChain(FilterChain chain) {
-      this.chain = chain;
-    }
-
-    @Override
-    public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
-      if (iterator == null) {
-        iterator = filters.iterator();
-      }
-      if (iterator.hasNext()) {
-        iterator.next().doFilter(request, response, this);
-      } else {
-        chain.doFilter(request, response);
-      }
-    }
-
-    public void addFilter(Filter filter) {
-      Preconditions.checkState(iterator == null);
-      filters.add(filter);
-    }
-  }
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/PlatformServletContextListener.java b/server/sonar-server/src/main/java/org/sonar/server/platform/PlatformServletContextListener.java
deleted file mode 100644 (file)
index 83a6a75..0000000
+++ /dev/null
@@ -1,70 +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.server.platform;
-
-import com.google.common.base.Throwables;
-import java.util.Enumeration;
-import java.util.Properties;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletContextEvent;
-import javax.servlet.ServletContextListener;
-
-public final class PlatformServletContextListener implements ServletContextListener {
-
-  static final String STARTED_ATTRIBUTE = "sonarqube.started";
-
-  @Override
-  public void contextInitialized(ServletContextEvent event) {
-    try {
-      Properties props = new Properties();
-      ServletContext servletContext = event.getServletContext();
-      Enumeration<String> paramKeys = servletContext.getInitParameterNames();
-      while (paramKeys.hasMoreElements()) {
-        String key = paramKeys.nextElement();
-        props.put(key, servletContext.getInitParameter(key));
-      }
-      Platform.getInstance().init(props, servletContext);
-      Platform.getInstance().doStart();
-      event.getServletContext().setAttribute(STARTED_ATTRIBUTE, Boolean.TRUE);
-
-    } catch (Throwable t) {
-      // Tomcat 7 "limitations":
-      // - server does not stop if webapp fails at startup
-      // - the second listener for jruby on rails is started even if this listener fails. It generates
-      // unexpected errors
-      stopQuietly();
-      throw Throwables.propagate(t);
-    }
-  }
-
-  private void stopQuietly() {
-    try {
-      Platform.getInstance().doStop();
-    } catch (Exception e) {
-      // ignored, but an error during startup generally prevents pico to be correctly stopped
-    }
-  }
-
-  @Override
-  public void contextDestroyed(ServletContextEvent event) {
-    Platform.getInstance().doStop();
-  }
-
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ProfilingFilter.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ProfilingFilter.java
deleted file mode 100644 (file)
index 9aa397f..0000000
+++ /dev/null
@@ -1,115 +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.server.platform;
-
-import com.google.common.collect.ImmutableSet;
-import org.apache.commons.lang.StringUtils;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.api.utils.log.Profiler;
-
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-
-import java.io.IOException;
-import java.util.Set;
-
-/**
- * <p>Profile HTTP requests using platform profiling utility.</p>
- * <p>To avoid profiling of requests for static resources, the <code>staticDirs</code>
- * filter parameter can be set in the servlet context descriptor. This parameter should
- * contain a comma-separated list of paths, starting at the context root;
- * requests on subpaths of these paths will not be profiled.</p>
- *
- * @since 4.1
- */
-public class ProfilingFilter implements Filter {
-
-  private static final String CONFIG_SEPARATOR = ",";
-  private static final String URL_SEPARATOR = "/";
-
-  private static final String MESSAGE_WITH_QUERY = "%s %s?%s";
-  private static final String MESSAGE_WITHOUT_QUERY = "%s %s";
-  public static final org.sonar.api.utils.log.Logger Logger = Loggers.get("http");
-
-  private String contextRoot;
-  private Set<String> staticResourceDirs;
-
-  @Override
-  public void init(FilterConfig filterConfig) throws ServletException {
-    contextRoot = filterConfig.getServletContext().getContextPath();
-
-    String staticResourcesConfig = filterConfig.getInitParameter("staticDirs");
-    if (StringUtils.isNotBlank(staticResourcesConfig)) {
-      staticResourceDirs = ImmutableSet.copyOf(staticResourcesConfig.split(CONFIG_SEPARATOR));
-    } else {
-      staticResourceDirs = ImmutableSet.of();
-    }
-  }
-
-  @Override
-  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
-    if (request instanceof HttpServletRequest) {
-      HttpServletRequest httpRequest = (HttpServletRequest) request;
-      String requestUri = httpRequest.getRequestURI();
-      String rootDir = getRootDir(requestUri);
-
-      if (staticResourceDirs.contains(rootDir)) {
-        // Static resource, not profiled
-        chain.doFilter(request, response);
-      } else {
-        Profiler profiler = Profiler.createIfDebug(Logger).start();
-        try {
-          chain.doFilter(request, response);
-        } finally {
-          if (profiler.isDebugEnabled()) {
-            String queryString = httpRequest.getQueryString();
-            String message = String.format(queryString == null ? MESSAGE_WITHOUT_QUERY : MESSAGE_WITH_QUERY, httpRequest.getMethod(), requestUri, queryString);
-            profiler.stopDebug(message);
-          }
-        }
-      }
-    } else {
-      // Not an HTTP request, not profiled
-      chain.doFilter(request, response);
-    }
-  }
-
-  private String getRootDir(String requestUri) {
-    String rootPath = "";
-    String localPath = StringUtils.substringAfter(requestUri, contextRoot);
-    if (localPath.startsWith(URL_SEPARATOR)) {
-      int secondSlash = localPath.indexOf(URL_SEPARATOR, 1);
-      if (secondSlash > 0) {
-        rootPath = URL_SEPARATOR + localPath.substring(1, secondSlash);
-      }
-    }
-    return rootPath;
-  }
-
-  @Override
-  public void destroy() {
-    // Nothing
-  }
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/RailsAppsDeployer.java b/server/sonar-server/src/main/java/org/sonar/server/platform/RailsAppsDeployer.java
deleted file mode 100644 (file)
index 2a5d0a3..0000000
+++ /dev/null
@@ -1,126 +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.server.platform;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Function;
-import java.io.File;
-import java.io.IOException;
-import javax.annotation.Nullable;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.lang.StringUtils;
-import org.picocontainer.Startable;
-import org.sonar.api.Plugin;
-import org.sonar.api.SonarPlugin;
-import org.sonar.api.platform.ServerFileSystem;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.core.platform.PluginInfo;
-import org.sonar.core.platform.PluginRepository;
-
-/**
- * Ruby on Rails requires the files to be on filesystem but not in Java classpath (JAR). This component extracts
- * all the needed files from plugins and copy them to $SONAR_HOME/temp
- *
- * @since 3.0
- */
-public class RailsAppsDeployer implements Startable {
-
-  private static final Logger LOG = Loggers.get(RailsAppsDeployer.class);
-  private static final String ROR_PATH = "org/sonar/ror/";
-
-  private final ServerFileSystem fs;
-  private final PluginRepository pluginRepository;
-
-  public RailsAppsDeployer(ServerFileSystem fs, PluginRepository pluginRepository) {
-    this.fs = fs;
-    this.pluginRepository = pluginRepository;
-  }
-
-  @Override
-  public void start() {
-    LOG.info("Deploying Ruby on Rails applications");
-    File appsDir = prepareRailsDirectory();
-
-    for (PluginInfo pluginInfo : pluginRepository.getPluginInfos()) {
-      String pluginKey = pluginInfo.getKey();
-      Plugin plugin = pluginRepository.getPluginInstance(pluginKey);
-      try {
-        deployRailsApp(appsDir, pluginKey, plugin.getClass().getClassLoader());
-      } catch (Exception e) {
-        throw new IllegalStateException(String.format("Fail to deploy Ruby on Rails application of plugin [%s]", pluginKey), e);
-      }
-    }
-  }
-
-  @Override
-  public void stop() {
-    // do nothing
-  }
-
-  @VisibleForTesting
-  File prepareRailsDirectory() {
-    File appsDir = new File(fs.getTempDir(), "ror");
-    prepareDir(appsDir);
-    return appsDir;
-  }
-
-  @VisibleForTesting
-  static void deployRailsApp(File appsDir, final String pluginKey, ClassLoader appClassLoader) {
-    if (hasRailsApp(pluginKey, appClassLoader)) {
-      LOG.info("Deploying app: " + pluginKey);
-      File appDir = new File(appsDir, pluginKey);
-      ClassLoaderUtils.copyResources(appClassLoader, pathToRubyInitFile(pluginKey), appDir, new Function<String, String>() {
-        @Override
-        public String apply(@Nullable String relativePath) {
-          // Relocate the deployed files :
-          // relativePath format is: org/sonar/ror/sqale/app/controllers/foo_controller.rb
-          // app path is: org/sonar/ror/sqale
-          // -> deployed file is app/controllers/foo_controller.rb
-          return StringUtils.substringAfter(relativePath, pluginKey + "/");
-        }
-      });
-    }
-  }
-
-  private static String pathToRubyInitFile(String pluginKey) {
-    return ROR_PATH + pluginKey + "/init.rb";
-  }
-
-  @VisibleForTesting
-  static boolean hasRailsApp(String pluginKey, ClassLoader classLoader) {
-    return classLoader.getResource(pathToRubyInitFile(pluginKey)) != null;
-  }
-
-  private void prepareDir(File appsDir) {
-    if (appsDir.exists() && appsDir.isDirectory()) {
-      try {
-        org.sonar.core.util.FileUtils.deleteDirectory(appsDir);
-      } catch (IOException e) {
-        throw new IllegalStateException("Fail to delete temp directory: " + appsDir, e);
-      }
-    }
-    try {
-      FileUtils.forceMkdir(appsDir);
-    } catch (IOException e) {
-      throw new IllegalStateException("Fail to create temp directory: " + appsDir, e);
-    }
-  }
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/RoutesFilter.java b/server/sonar-server/src/main/java/org/sonar/server/platform/RoutesFilter.java
deleted file mode 100644 (file)
index 2cb7ad6..0000000
+++ /dev/null
@@ -1,68 +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.server.platform;
-
-import static java.lang.String.format;
-
-import java.io.IOException;
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-public class RoutesFilter implements Filter {
-
-  private static final String EMPTY = "";
-  private static final String BATCH_WS = "/batch";
-  private static final String API_SOURCES_WS = "/api/sources";
-
-  @Override
-  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
-    HttpServletRequest request = (HttpServletRequest) servletRequest;
-    HttpServletResponse response = (HttpServletResponse) servletResponse;
-    String path = request.getRequestURI().replaceFirst(request.getContextPath(), EMPTY);
-    if (path.startsWith(BATCH_WS + "/") && path.endsWith(".jar")) {
-      // Scanner is still using /batch/file.jar url
-      response.sendRedirect(format("%s%s/file?name=%s", request.getContextPath(), BATCH_WS, path.replace(BATCH_WS + "/", EMPTY)));
-    } else if ("/batch_bootstrap/index".equals(path)) {
-      // Scanner is still using /batch_bootstrap url
-      response.sendRedirect(format("%s%s/index", request.getContextPath(), BATCH_WS));
-    } else if (API_SOURCES_WS.equals(path)) {
-      // SONAR-7852 /api/sources?resource url is still used
-      response.sendRedirect(format("%s%s/index?%s", request.getContextPath(), API_SOURCES_WS, request.getQueryString()));
-    } else {
-      chain.doFilter(request, response);
-    }
-  }
-
-  @Override
-  public void init(FilterConfig filterConfig) throws ServletException {
-    // Nothing
-  }
-
-  @Override
-  public void destroy() {
-    // Nothing
-  }
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/RubyRailsContextListener.java b/server/sonar-server/src/main/java/org/sonar/server/platform/RubyRailsContextListener.java
deleted file mode 100644 (file)
index 3f8fb0c..0000000
+++ /dev/null
@@ -1,51 +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.server.platform;
-
-import com.google.common.base.Throwables;
-import javax.servlet.ServletContextEvent;
-import org.jruby.rack.RackApplicationFactory;
-import org.jruby.rack.rails.RailsServletContextListener;
-import org.jruby.rack.servlet.ServletRackContext;
-
-/**
- * Overriding {@link RailsServletContextListener} allows to disable initialization of Ruby on Rails
- * environment when Java components fail to start.
- * See https://jira.sonarsource.com/browse/SONAR-6740
- */
-public class RubyRailsContextListener extends RailsServletContextListener {
-
-  @Override
-  public void contextInitialized(ServletContextEvent event) {
-    if (event.getServletContext().getAttribute(PlatformServletContextListener.STARTED_ATTRIBUTE) != null) {
-      super.contextInitialized(event);
-    }
-  }
-
-  // Always stop server when an error is raised during startup.
-  // By default Rack only logs an error (see org.jruby.rack.RackServletContextListener#handleInitializationException()).
-  // Rack propagates exceptions if the properties jruby.rack.exception or jruby.rack.error are set to true.
-  // Unfortunately we didn't succeed in defining these properties, so the method is overridden here.
-  // Initial need: SONAR-6171
-  @Override
-  protected void handleInitializationException(Exception e, RackApplicationFactory factory, ServletRackContext rackContext) {
-    throw Throwables.propagate(e);
-  }
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/SecurityServletFilter.java b/server/sonar-server/src/main/java/org/sonar/server/platform/SecurityServletFilter.java
deleted file mode 100644 (file)
index b929a8c..0000000
+++ /dev/null
@@ -1,78 +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.server.platform;
-
-import com.google.common.collect.ImmutableSet;
-import java.io.IOException;
-import java.util.Set;
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * This servlet filter sets response headers that enable browser protection against several classes if Web attacks.
- * The list of headers is mirrored in environment.rb as a workaround to Rack swallowing the headers..
- */
-public class SecurityServletFilter implements Filter {
-
-  private static final Set<String> ALLOWED_HTTP_METHODS = ImmutableSet.of("DELETE", "GET", "HEAD", "POST", "PUT");
-
-  @Override
-  public void init(FilterConfig filterConfig) throws ServletException {
-    // nothing
-  }
-
-  @Override
-  public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
-    doHttpFilter((HttpServletRequest) req, (HttpServletResponse) resp, chain);
-  }
-
-  private static void doHttpFilter(HttpServletRequest httpRequest, HttpServletResponse httpResponse, FilterChain chain) throws IOException, ServletException {
-    // SONAR-6881 Disable OPTIONS and TRACE methods
-    if (!ALLOWED_HTTP_METHODS.contains(httpRequest.getMethod())) {
-      httpResponse.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
-      return;
-    }
-
-    chain.doFilter(httpRequest, httpResponse);
-
-    // Clickjacking protection
-    // See https://www.owasp.org/index.php/Clickjacking_Protection_for_Java_EE
-    httpResponse.addHeader("X-Frame-Options", "SAMEORIGIN");
-
-    // Cross-site scripting
-    // See https://www.owasp.org/index.php/List_of_useful_HTTP_headers
-    httpResponse.addHeader("X-XSS-Protection", "1; mode=block");
-
-    // MIME-sniffing
-    // See https://www.owasp.org/index.php/List_of_useful_HTTP_headers
-    httpResponse.addHeader("X-Content-Type-Options", "nosniff");
-  }
-
-  @Override
-  public void destroy() {
-    // nothing
-  }
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerFileSystem.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerFileSystem.java
new file mode 100644 (file)
index 0000000..8fe21c2
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * 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.server.platform;
+
+import java.io.File;
+
+/**
+ * Replaces the incomplete {@link org.sonar.api.platform.ServerFileSystem} as many directories can't be
+ * published in API.
+ *
+ * @since 6.0
+ */
+public interface ServerFileSystem {
+
+  /**
+   * Directory that contains the persisted data to be kept on restarts and upgrades.
+   * @return an existing directory
+   */
+  File getDataDir();
+
+  /**
+   * Directory accessible by scanners through web server
+   * @return a non-null directory that MAY exist
+   */
+  File getDeployDir();
+
+  /**
+   * Root directory of the server installation
+   * @return an existing directory
+   */
+  File getHomeDir();
+
+  /**
+   * Temporary directory, clean up on restarts
+   * @return an existing directory
+   */
+  File getTempDir();
+
+  /**
+   * Files of plugins published by web server for scanners
+   * @return a non-null directory that MAY exist
+   */
+  File getDeployedPluginsDir();
+
+  /**
+   * Directory of plugins downloaded through update center. Files
+   * will be moved to {@link #getInstalledPluginsDir()} on startup.
+   * @return an existing directory
+   */
+  File getDownloadedPluginsDir();
+
+  /**
+   * Directory of currently installed plugins. Used at startup.
+   * @return an existing directory
+   */
+  File getInstalledPluginsDir();
+
+  /**
+   * Directory of the plugins packaged by default with application. These
+   * plugins are installed during the first fresh startup.
+   * @return an existing directory
+   */
+  File getBundledPluginsDir();
+
+  /**
+   * The file listing all the installed plugins. Used by scanner only.
+   * @return an existing file
+   */
+  File getPluginIndex();
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerFileSystemImpl.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerFileSystemImpl.java
new file mode 100644 (file)
index 0000000..258568d
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * 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.server.platform;
+
+import java.io.File;
+import org.picocontainer.Startable;
+import org.sonar.api.config.Settings;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.process.ProcessProperties;
+import org.sonar.server.app.TomcatContexts;
+
+import static java.util.Objects.requireNonNull;
+
+public class ServerFileSystemImpl implements ServerFileSystem, org.sonar.api.platform.ServerFileSystem, Startable {
+
+  private static final Logger LOGGER = Loggers.get(ServerFileSystemImpl.class);
+
+  private final File homeDir;
+  private final File tempDir;
+  private final File dataDir;
+  private final File deployDir;
+
+  public ServerFileSystemImpl(Settings settings) {
+    this.homeDir = new File(requireNonNull(settings.getString(ProcessProperties.PATH_HOME)));
+    this.tempDir = new File(requireNonNull(settings.getString(ProcessProperties.PATH_TEMP)));
+    this.dataDir = new File(requireNonNull(settings.getString(ProcessProperties.PATH_DATA)));
+    this.deployDir = new File(this.dataDir, TomcatContexts.WEB_DEPLOY_PATH_RELATIVE_TO_DATA_DIR);
+  }
+
+  @Override
+  public void start() {
+    LOGGER.info("SonarQube home: " + homeDir.getAbsolutePath());
+  }
+
+  @Override
+  public void stop() {
+    // do nothing
+  }
+
+  @Override
+  public File getHomeDir() {
+    return homeDir;
+  }
+
+  @Override
+  public File getTempDir() {
+    return tempDir;
+  }
+
+  @Override
+  public File getDataDir() {
+    return dataDir;
+  }
+
+  @Override
+  public File getDeployDir() {
+    return deployDir;
+  }
+
+  @Override
+  public File getDeployedPluginsDir() {
+    return new File(getDeployDir(), "plugins");
+  }
+
+  @Override
+  public File getDownloadedPluginsDir() {
+    return new File(getHomeDir(), "extensions/downloads");
+  }
+
+  @Override
+  public File getInstalledPluginsDir() {
+    return new File(getHomeDir(), "extensions/plugins");
+  }
+
+  @Override
+  public File getBundledPluginsDir() {
+    return new File(getHomeDir(), "lib/bundled-plugins");
+  }
+
+  @Override
+  public File getPluginIndex() {
+    return new File(getDeployDir(), "plugins/index.txt");
+  }
+
+}
index a3a834077c5230ff0f24a27885ba8f2e7403f59f..e9a9e71411508a19f9dfa40bcd04c141f3da6777 100644 (file)
@@ -40,7 +40,6 @@ import org.sonar.api.platform.Server;
 import org.sonar.api.utils.log.Logger;
 import org.sonar.api.utils.log.Loggers;
 import org.sonar.process.ProcessProperties;
-import org.sonar.server.app.TomcatContexts;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
@@ -65,7 +64,6 @@ public final class ServerImpl extends Server implements Startable {
   private String version;
   private String implementationBuild;
   private File sonarHome;
-  private File deployDir;
   private String contextPath;
 
   public ServerImpl(Settings settings) {
@@ -94,8 +92,6 @@ public final class ServerImpl extends Server implements Startable {
         throw new IllegalStateException("SonarQube home directory is not valid");
       }
 
-      deployDir = new File(settings.getString(ProcessProperties.PATH_DATA), TomcatContexts.WEB_DEPLOY_PATH_RELATIVE_TO_DATA_DIR);
-
       contextPath = StringUtils.defaultIfBlank(settings.getString(PROPERTY_CONTEXT), "")
         // Remove trailing slashes
         .replaceFirst("(\\/+)$", "");
@@ -144,7 +140,7 @@ public final class ServerImpl extends Server implements Startable {
   @Override
   @CheckForNull
   public File getDeployDir() {
-    return deployDir;
+    return null;
   }
 
   @Override
index 1927f95f51ffdf949cdf4972d098fa1e87bdbd17..7910ed57eb685175b5af2ca416dc695891ab34a6 100644 (file)
  */
 package org.sonar.server.platform;
 
+import java.io.File;
+import java.io.IOException;
 import org.apache.commons.io.FileUtils;
 import org.picocontainer.injectors.ProviderAdapter;
-import org.sonar.api.platform.ServerFileSystem;
 import org.sonar.api.utils.TempFolder;
 import org.sonar.api.utils.internal.DefaultTempFolder;
 
-import java.io.File;
-import java.io.IOException;
-
 public class TempFolderProvider extends ProviderAdapter {
 
   private TempFolder tempFolder;
index 4be5c21f58b6d22f6eb8c29bf927a4bf38618222..3b44379c826afc5c949d3207fdf695e188d2202a 100644 (file)
@@ -1,3 +1,22 @@
+/*
+ * 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.server.platform.cluster;
 
 import com.google.common.collect.ImmutableList;
index 58053ce34985508cf5f8d902195052c80fe6db2d..54bcf9d83e920c739ea65faf7404cf541e75a2b9 100644 (file)
@@ -43,7 +43,7 @@ import org.sonar.server.app.RestartFlagHolderImpl;
 import org.sonar.server.db.EmbeddedDatabaseFactory;
 import org.sonar.server.issue.index.IssueIndex;
 import org.sonar.server.platform.DatabaseServerCompatibility;
-import org.sonar.server.platform.DefaultServerFileSystem;
+import org.sonar.server.platform.ServerFileSystemImpl;
 import org.sonar.server.platform.Platform;
 import org.sonar.server.platform.ServerImpl;
 import org.sonar.server.platform.ServerSettingsImpl;
@@ -89,7 +89,7 @@ public class PlatformLevel1 extends PlatformLevel {
       DatabaseServerCompatibility.class,
       DatabaseVersion.class,
       PurgeProfiler.class,
-      DefaultServerFileSystem.class,
+      ServerFileSystemImpl.class,
       SemaphoresImpl.class,
       TempFolderCleaner.class,
       new TempFolderProvider(),
index 996aa970dbe813a023a1e30a4dd12dbc89a45f39..4c1455e2f07920a7a0846adb326bdf14961bc886 100644 (file)
@@ -31,7 +31,7 @@ import org.sonar.server.db.migrations.DatabaseMigrator;
 import org.sonar.server.db.migrations.PlatformDatabaseMigration;
 import org.sonar.server.db.migrations.PlatformDatabaseMigrationExecutorServiceImpl;
 import org.sonar.server.platform.DefaultServerUpgradeStatus;
-import org.sonar.server.platform.RailsAppsDeployer;
+import org.sonar.server.platform.web.RailsAppsDeployer;
 import org.sonar.server.plugins.InstalledPluginReferentialFactory;
 import org.sonar.server.plugins.ServerPluginJarExploder;
 import org.sonar.server.plugins.ServerPluginRepository;
index ae8a6c8ee431a8ed9b37ba5424ff964def602480..7ba5aec89e1851935e1995778af4c35cba2db3d3 100644 (file)
@@ -36,7 +36,7 @@ import org.sonar.server.startup.RegisterDashboards;
 import org.sonar.server.startup.RegisterMetrics;
 import org.sonar.server.startup.RegisterNewMeasureFilters;
 import org.sonar.server.startup.RegisterPermissionTemplates;
-import org.sonar.server.startup.RegisterServletFilters;
+import org.sonar.server.platform.web.RegisterServletFilters;
 import org.sonar.server.startup.RenameDeprecatedPropertyKeys;
 import org.sonar.server.startup.RenameIssueWidgets;
 import org.sonar.server.user.DoPrivileged;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/web/MasterServletFilter.java b/server/sonar-server/src/main/java/org/sonar/server/platform/web/MasterServletFilter.java
new file mode 100644 (file)
index 0000000..76f3abd
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * 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.server.platform.web;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.api.web.ServletFilter;
+import org.sonar.server.platform.Platform;
+
+/**
+ * Inspired by http://stackoverflow.com/a/7592883/229031
+ */
+public class MasterServletFilter implements Filter {
+
+  public static volatile MasterServletFilter INSTANCE;
+  private ServletFilter[] filters;
+  private FilterConfig config;
+
+  public MasterServletFilter() {
+    if (INSTANCE != null) {
+      throw new IllegalStateException("Servlet filter " + getClass().getName() + " is already instantiated");
+    }
+    INSTANCE = this;
+  }
+
+  @Override
+  public void init(FilterConfig config) throws ServletException {
+    // Filters are already available in picocontainer unless a database migration is required. See
+    // org.sonar.server.startup.RegisterServletFilters.
+    init(config, Platform.getInstance().getContainer().getComponentsByType(ServletFilter.class));
+  }
+
+  void init(FilterConfig config, List<ServletFilter> filters) throws ServletException {
+    this.config = config;
+    initFilters(filters);
+  }
+
+  public void initFilters(List<ServletFilter> filterExtensions) throws ServletException {
+    List<ServletFilter> filterList = Lists.newArrayList();
+    for (ServletFilter extension : filterExtensions) {
+      try {
+        Loggers.get(MasterServletFilter.class).info(String.format("Initializing servlet filter %s [pattern=%s]", extension, extension.doGetPattern()));
+        extension.init(config);
+        filterList.add(extension);
+      } catch (Exception e) {
+        throw new IllegalStateException("Fail to initialize servlet filter: " + extension + ". Message: " + e.getMessage(), e);
+      }
+    }
+    filters = filterList.toArray(new ServletFilter[filterList.size()]);
+  }
+
+  @Override
+  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
+    HttpServletRequest hsr = (HttpServletRequest) request;
+    if (filters.length == 0) {
+      chain.doFilter(request, response);
+    } else {
+      String path = hsr.getRequestURI().replaceFirst(hsr.getContextPath(), "");
+      GodFilterChain godChain = new GodFilterChain(chain);
+
+      for (ServletFilter filter : filters) {
+        if (filter.doGetPattern().matches(path)) {
+          godChain.addFilter(filter);
+        }
+      }
+      godChain.doFilter(request, response);
+    }
+  }
+
+  @Override
+  public void destroy() {
+    for (ServletFilter filter : filters) {
+      filter.destroy();
+    }
+  }
+
+  @VisibleForTesting
+  ServletFilter[] getFilters() {
+    return filters;
+  }
+
+  private static final class GodFilterChain implements FilterChain {
+    private FilterChain chain;
+    private List<Filter> filters = Lists.newLinkedList();
+    private Iterator<Filter> iterator;
+
+    public GodFilterChain(FilterChain chain) {
+      this.chain = chain;
+    }
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
+      if (iterator == null) {
+        iterator = filters.iterator();
+      }
+      if (iterator.hasNext()) {
+        iterator.next().doFilter(request, response, this);
+      } else {
+        chain.doFilter(request, response);
+      }
+    }
+
+    public void addFilter(Filter filter) {
+      Preconditions.checkState(iterator == null);
+      filters.add(filter);
+    }
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/web/PlatformServletContextListener.java b/server/sonar-server/src/main/java/org/sonar/server/platform/web/PlatformServletContextListener.java
new file mode 100644 (file)
index 0000000..d833041
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * 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.server.platform.web;
+
+import com.google.common.base.Throwables;
+import java.util.Enumeration;
+import java.util.Properties;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import org.sonar.server.platform.Platform;
+
+public final class PlatformServletContextListener implements ServletContextListener {
+
+  static final String STARTED_ATTRIBUTE = "sonarqube.started";
+
+  @Override
+  public void contextInitialized(ServletContextEvent event) {
+    try {
+      Properties props = new Properties();
+      ServletContext servletContext = event.getServletContext();
+      Enumeration<String> paramKeys = servletContext.getInitParameterNames();
+      while (paramKeys.hasMoreElements()) {
+        String key = paramKeys.nextElement();
+        props.put(key, servletContext.getInitParameter(key));
+      }
+      Platform.getInstance().init(props, servletContext);
+      Platform.getInstance().doStart();
+      event.getServletContext().setAttribute(STARTED_ATTRIBUTE, Boolean.TRUE);
+
+    } catch (Throwable t) {
+      // Tomcat 7 "limitations":
+      // - server does not stop if webapp fails at startup
+      // - the second listener for jruby on rails is started even if this listener fails. It generates
+      // unexpected errors
+      stopQuietly();
+      throw Throwables.propagate(t);
+    }
+  }
+
+  private void stopQuietly() {
+    try {
+      Platform.getInstance().doStop();
+    } catch (Exception e) {
+      // ignored, but an error during startup generally prevents pico to be correctly stopped
+    }
+  }
+
+  @Override
+  public void contextDestroyed(ServletContextEvent event) {
+    Platform.getInstance().doStop();
+  }
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/web/ProfilingFilter.java b/server/sonar-server/src/main/java/org/sonar/server/platform/web/ProfilingFilter.java
new file mode 100644 (file)
index 0000000..8728a7b
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * 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.server.platform.web;
+
+import com.google.common.collect.ImmutableSet;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.api.utils.log.Profiler;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+
+import java.io.IOException;
+import java.util.Set;
+
+/**
+ * <p>Profile HTTP requests using platform profiling utility.</p>
+ * <p>To avoid profiling of requests for static resources, the <code>staticDirs</code>
+ * filter parameter can be set in the servlet context descriptor. This parameter should
+ * contain a comma-separated list of paths, starting at the context root;
+ * requests on subpaths of these paths will not be profiled.</p>
+ *
+ * @since 4.1
+ */
+public class ProfilingFilter implements Filter {
+
+  private static final String CONFIG_SEPARATOR = ",";
+  private static final String URL_SEPARATOR = "/";
+
+  private static final String MESSAGE_WITH_QUERY = "%s %s?%s";
+  private static final String MESSAGE_WITHOUT_QUERY = "%s %s";
+  public static final org.sonar.api.utils.log.Logger Logger = Loggers.get("http");
+
+  private String contextRoot;
+  private Set<String> staticResourceDirs;
+
+  @Override
+  public void init(FilterConfig filterConfig) throws ServletException {
+    contextRoot = filterConfig.getServletContext().getContextPath();
+
+    String staticResourcesConfig = filterConfig.getInitParameter("staticDirs");
+    if (StringUtils.isNotBlank(staticResourcesConfig)) {
+      staticResourceDirs = ImmutableSet.copyOf(staticResourcesConfig.split(CONFIG_SEPARATOR));
+    } else {
+      staticResourceDirs = ImmutableSet.of();
+    }
+  }
+
+  @Override
+  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+    if (request instanceof HttpServletRequest) {
+      HttpServletRequest httpRequest = (HttpServletRequest) request;
+      String requestUri = httpRequest.getRequestURI();
+      String rootDir = getRootDir(requestUri);
+
+      if (staticResourceDirs.contains(rootDir)) {
+        // Static resource, not profiled
+        chain.doFilter(request, response);
+      } else {
+        Profiler profiler = Profiler.createIfDebug(Logger).start();
+        try {
+          chain.doFilter(request, response);
+        } finally {
+          if (profiler.isDebugEnabled()) {
+            String queryString = httpRequest.getQueryString();
+            String message = String.format(queryString == null ? MESSAGE_WITHOUT_QUERY : MESSAGE_WITH_QUERY, httpRequest.getMethod(), requestUri, queryString);
+            profiler.stopDebug(message);
+          }
+        }
+      }
+    } else {
+      // Not an HTTP request, not profiled
+      chain.doFilter(request, response);
+    }
+  }
+
+  private String getRootDir(String requestUri) {
+    String rootPath = "";
+    String localPath = StringUtils.substringAfter(requestUri, contextRoot);
+    if (localPath.startsWith(URL_SEPARATOR)) {
+      int secondSlash = localPath.indexOf(URL_SEPARATOR, 1);
+      if (secondSlash > 0) {
+        rootPath = URL_SEPARATOR + localPath.substring(1, secondSlash);
+      }
+    }
+    return rootPath;
+  }
+
+  @Override
+  public void destroy() {
+    // Nothing
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/web/RailsAppsDeployer.java b/server/sonar-server/src/main/java/org/sonar/server/platform/web/RailsAppsDeployer.java
new file mode 100644 (file)
index 0000000..9de419f
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * 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.server.platform.web;
+
+import com.google.common.annotations.VisibleForTesting;
+import java.io.File;
+import java.io.IOException;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.StringUtils;
+import org.picocontainer.Startable;
+import org.sonar.api.Plugin;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.core.platform.PluginInfo;
+import org.sonar.core.platform.PluginRepository;
+import org.sonar.server.platform.ServerFileSystem;
+import org.sonar.server.util.ClassLoaderUtils;
+
+/**
+ * Ruby on Rails requires the files to be on filesystem but not in Java classpath (JAR). This component extracts
+ * all the needed files from plugins and copy them to $SONAR_HOME/temp
+ *
+ * @since 3.0
+ */
+public class RailsAppsDeployer implements Startable {
+
+  private static final Logger LOG = Loggers.get(RailsAppsDeployer.class);
+  private static final String ROR_PATH = "org/sonar/ror/";
+
+  private final ServerFileSystem fs;
+  private final PluginRepository pluginRepository;
+
+  public RailsAppsDeployer(ServerFileSystem fs, PluginRepository pluginRepository) {
+    this.fs = fs;
+    this.pluginRepository = pluginRepository;
+  }
+
+  @Override
+  public void start() {
+    LOG.info("Deploying Ruby on Rails applications");
+    File appsDir = prepareRailsDirectory();
+
+    for (PluginInfo pluginInfo : pluginRepository.getPluginInfos()) {
+      String pluginKey = pluginInfo.getKey();
+      Plugin plugin = pluginRepository.getPluginInstance(pluginKey);
+      try {
+        deployRailsApp(appsDir, pluginKey, plugin.getClass().getClassLoader());
+      } catch (Exception e) {
+        throw new IllegalStateException(String.format("Fail to deploy Ruby on Rails application of plugin [%s]", pluginKey), e);
+      }
+    }
+  }
+
+  @Override
+  public void stop() {
+    // do nothing
+  }
+
+  @VisibleForTesting
+  File prepareRailsDirectory() {
+    File appsDir = new File(fs.getTempDir(), "ror");
+    prepareDir(appsDir);
+    return appsDir;
+  }
+
+  @VisibleForTesting
+  static void deployRailsApp(File appsDir, final String pluginKey, ClassLoader appClassLoader) {
+    if (hasRailsApp(pluginKey, appClassLoader)) {
+      LOG.info("Deploying app: " + pluginKey);
+      File appDir = new File(appsDir, pluginKey);
+      ClassLoaderUtils.copyResources(appClassLoader, pathToRubyInitFile(pluginKey), appDir, relativePath -> {
+        // Relocate the deployed files :
+        // relativePath format is: org/sonar/ror/sqale/app/controllers/foo_controller.rb
+        // app path is: org/sonar/ror/sqale
+        // -> deployed file is app/controllers/foo_controller.rb
+        return StringUtils.substringAfter(relativePath, pluginKey + "/");
+      });
+    }
+  }
+
+  private static String pathToRubyInitFile(String pluginKey) {
+    return ROR_PATH + pluginKey + "/init.rb";
+  }
+
+  @VisibleForTesting
+  static boolean hasRailsApp(String pluginKey, ClassLoader classLoader) {
+    return classLoader.getResource(pathToRubyInitFile(pluginKey)) != null;
+  }
+
+  private void prepareDir(File appsDir) {
+    if (appsDir.exists() && appsDir.isDirectory()) {
+      try {
+        org.sonar.core.util.FileUtils.deleteDirectory(appsDir);
+      } catch (IOException e) {
+        throw new IllegalStateException("Fail to delete temp directory: " + appsDir, e);
+      }
+    }
+    try {
+      FileUtils.forceMkdir(appsDir);
+    } catch (IOException e) {
+      throw new IllegalStateException("Fail to create temp directory: " + appsDir, e);
+    }
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/web/RegisterServletFilters.java b/server/sonar-server/src/main/java/org/sonar/server/platform/web/RegisterServletFilters.java
new file mode 100644 (file)
index 0000000..3b0dba4
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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.server.platform.web;
+
+import java.util.Arrays;
+import javax.servlet.ServletException;
+import org.sonar.api.web.ServletFilter;
+
+/**
+ * @since 3.5
+ */
+public class RegisterServletFilters {
+  private final ServletFilter[] filters;
+
+  public RegisterServletFilters(ServletFilter[] filters) {
+    this.filters = filters;
+  }
+
+  public RegisterServletFilters() {
+    this(new ServletFilter[0]);
+  }
+
+  public void start() throws ServletException {
+    if (MasterServletFilter.INSTANCE != null) {
+      // Probably a database upgrade. MasterSlaveFilter was instantiated by the servlet container
+      // while picocontainer was not completely up.
+      // See https://jira.sonarsource.com/browse/SONAR-3612
+      MasterServletFilter.INSTANCE.initFilters(Arrays.asList(filters));
+    }
+  }
+}
+
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/web/RoutesFilter.java b/server/sonar-server/src/main/java/org/sonar/server/platform/web/RoutesFilter.java
new file mode 100644 (file)
index 0000000..5de1bd2
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * 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.server.platform.web;
+
+import static java.lang.String.format;
+
+import java.io.IOException;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class RoutesFilter implements Filter {
+
+  private static final String EMPTY = "";
+  private static final String BATCH_WS = "/batch";
+  private static final String API_SOURCES_WS = "/api/sources";
+
+  @Override
+  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
+    HttpServletRequest request = (HttpServletRequest) servletRequest;
+    HttpServletResponse response = (HttpServletResponse) servletResponse;
+    String path = request.getRequestURI().replaceFirst(request.getContextPath(), EMPTY);
+    if (path.startsWith(BATCH_WS + "/") && path.endsWith(".jar")) {
+      // Scanner is still using /batch/file.jar url
+      response.sendRedirect(format("%s%s/file?name=%s", request.getContextPath(), BATCH_WS, path.replace(BATCH_WS + "/", EMPTY)));
+    } else if ("/batch_bootstrap/index".equals(path)) {
+      // Scanner is still using /batch_bootstrap url
+      response.sendRedirect(format("%s%s/index", request.getContextPath(), BATCH_WS));
+    } else if (API_SOURCES_WS.equals(path)) {
+      // SONAR-7852 /api/sources?resource url is still used
+      response.sendRedirect(format("%s%s/index?%s", request.getContextPath(), API_SOURCES_WS, request.getQueryString()));
+    } else {
+      chain.doFilter(request, response);
+    }
+  }
+
+  @Override
+  public void init(FilterConfig filterConfig) throws ServletException {
+    // Nothing
+  }
+
+  @Override
+  public void destroy() {
+    // Nothing
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/web/RubyRailsContextListener.java b/server/sonar-server/src/main/java/org/sonar/server/platform/web/RubyRailsContextListener.java
new file mode 100644 (file)
index 0000000..bdc957b
--- /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.server.platform.web;
+
+import com.google.common.base.Throwables;
+import javax.servlet.ServletContextEvent;
+import org.jruby.rack.RackApplicationFactory;
+import org.jruby.rack.rails.RailsServletContextListener;
+import org.jruby.rack.servlet.ServletRackContext;
+
+/**
+ * Overriding {@link RailsServletContextListener} allows to disable initialization of Ruby on Rails
+ * environment when Java components fail to start.
+ * See https://jira.sonarsource.com/browse/SONAR-6740
+ */
+public class RubyRailsContextListener extends RailsServletContextListener {
+
+  @Override
+  public void contextInitialized(ServletContextEvent event) {
+    if (event.getServletContext().getAttribute(PlatformServletContextListener.STARTED_ATTRIBUTE) != null) {
+      super.contextInitialized(event);
+    }
+  }
+
+  // Always stop server when an error is raised during startup.
+  // By default Rack only logs an error (see org.jruby.rack.RackServletContextListener#handleInitializationException()).
+  // Rack propagates exceptions if the properties jruby.rack.exception or jruby.rack.error are set to true.
+  // Unfortunately we didn't succeed in defining these properties, so the method is overridden here.
+  // Initial need: SONAR-6171
+  @Override
+  protected void handleInitializationException(Exception e, RackApplicationFactory factory, ServletRackContext rackContext) {
+    throw Throwables.propagate(e);
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/web/SecurityServletFilter.java b/server/sonar-server/src/main/java/org/sonar/server/platform/web/SecurityServletFilter.java
new file mode 100644 (file)
index 0000000..a91d29e
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * 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.server.platform.web;
+
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.util.Set;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * This servlet filter sets response headers that enable browser protection against several classes if Web attacks.
+ * The list of headers is mirrored in environment.rb as a workaround to Rack swallowing the headers..
+ */
+public class SecurityServletFilter implements Filter {
+
+  private static final Set<String> ALLOWED_HTTP_METHODS = ImmutableSet.of("DELETE", "GET", "HEAD", "POST", "PUT");
+
+  @Override
+  public void init(FilterConfig filterConfig) throws ServletException {
+    // nothing
+  }
+
+  @Override
+  public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
+    doHttpFilter((HttpServletRequest) req, (HttpServletResponse) resp, chain);
+  }
+
+  private static void doHttpFilter(HttpServletRequest httpRequest, HttpServletResponse httpResponse, FilterChain chain) throws IOException, ServletException {
+    // SONAR-6881 Disable OPTIONS and TRACE methods
+    if (!ALLOWED_HTTP_METHODS.contains(httpRequest.getMethod())) {
+      httpResponse.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
+      return;
+    }
+
+    chain.doFilter(httpRequest, httpResponse);
+
+    // Clickjacking protection
+    // See https://www.owasp.org/index.php/Clickjacking_Protection_for_Java_EE
+    httpResponse.addHeader("X-Frame-Options", "SAMEORIGIN");
+
+    // Cross-site scripting
+    // See https://www.owasp.org/index.php/List_of_useful_HTTP_headers
+    httpResponse.addHeader("X-XSS-Protection", "1; mode=block");
+
+    // MIME-sniffing
+    // See https://www.owasp.org/index.php/List_of_useful_HTTP_headers
+    httpResponse.addHeader("X-Content-Type-Options", "nosniff");
+  }
+
+  @Override
+  public void destroy() {
+    // nothing
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/web/package-info.java b/server/sonar-server/src/main/java/org/sonar/server/platform/web/package-info.java
new file mode 100644 (file)
index 0000000..5bdbd56
--- /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.server.platform.web;
+
+import javax.annotation.ParametersAreNonnullByDefault;
index 436e657efa444b9c762cd0e0d8ef3df422f1552f..0b400478ad02844772a054f48033a64a9b2c0493 100644 (file)
@@ -35,7 +35,7 @@ import org.sonar.api.utils.log.Logger;
 import org.sonar.api.utils.log.Loggers;
 import org.sonar.core.platform.PluginInfo;
 import org.sonar.server.exceptions.BadRequestException;
-import org.sonar.server.platform.DefaultServerFileSystem;
+import org.sonar.server.platform.ServerFileSystem;
 import org.sonar.updatecenter.common.Release;
 import org.sonar.updatecenter.common.UpdateCenter;
 import org.sonar.updatecenter.common.Version;
@@ -65,7 +65,7 @@ public class PluginDownloader implements Startable {
   private final File downloadDir;
 
   public PluginDownloader(UpdateCenterMatrixFactory updateCenterMatrixFactory, HttpDownloader downloader,
-    DefaultServerFileSystem fileSystem) {
+    ServerFileSystem fileSystem) {
     this.updateCenterMatrixFactory = updateCenterMatrixFactory;
     this.downloader = downloader;
     this.downloadDir = fileSystem.getDownloadedPluginsDir();
index 8f6afbea37399bce9e9ab7205b6fb2fd15629a7b..5b630e26b5560d1da073ba70eaa2c762a80a2e4c 100644 (file)
@@ -27,7 +27,7 @@ import org.sonar.api.utils.ZipUtils;
 import org.sonar.core.platform.ExplodedPlugin;
 import org.sonar.core.platform.PluginInfo;
 import org.sonar.core.platform.PluginJarExploder;
-import org.sonar.server.platform.DefaultServerFileSystem;
+import org.sonar.server.platform.ServerFileSystem;
 
 import static org.apache.commons.io.FileUtils.forceMkdir;
 
@@ -35,9 +35,9 @@ import static org.apache.commons.io.FileUtils.forceMkdir;
 @ComputeEngineSide
 public class ServerPluginJarExploder extends PluginJarExploder {
 
-  private final DefaultServerFileSystem fs;
+  private final ServerFileSystem fs;
 
-  public ServerPluginJarExploder(DefaultServerFileSystem fs) {
+  public ServerPluginJarExploder(ServerFileSystem fs) {
     this.fs = fs;
   }
 
index 341bdc998373e724c93958cb59dea007ff313a5a..47e3bc584e02f816d6d8e103359d770257744b1d 100644 (file)
@@ -48,7 +48,7 @@ import org.sonar.api.utils.log.Loggers;
 import org.sonar.core.platform.PluginInfo;
 import org.sonar.core.platform.PluginLoader;
 import org.sonar.core.platform.PluginRepository;
-import org.sonar.server.platform.DefaultServerFileSystem;
+import org.sonar.server.platform.ServerFileSystem;
 import org.sonar.updatecenter.common.Version;
 
 import static com.google.common.base.Preconditions.checkArgument;
@@ -84,7 +84,7 @@ public class ServerPluginRepository implements PluginRepository, Startable {
   private static final String NOT_STARTED_YET = "not started yet";
 
   private final Server server;
-  private final DefaultServerFileSystem fs;
+  private final ServerFileSystem fs;
   private final ServerUpgradeStatus upgradeStatus;
   private final PluginLoader loader;
   private final AtomicBoolean started = new AtomicBoolean(false);
@@ -95,7 +95,7 @@ public class ServerPluginRepository implements PluginRepository, Startable {
   private final Map<String, Plugin> pluginInstancesByKeys = new HashMap<>();
 
   public ServerPluginRepository(Server server, ServerUpgradeStatus upgradeStatus,
-    DefaultServerFileSystem fs, PluginLoader loader) {
+    ServerFileSystem fs, PluginLoader loader) {
     this.server = server;
     this.upgradeStatus = upgradeStatus;
     this.fs = fs;
index 60537ad39e99b3319523a6865a3ac05cf94beb43..3e13f0472fbabb530d7b9a7f793a7ebee6b26936 100644 (file)
  */
 package org.sonar.server.startup;
 
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.nio.charset.StandardCharsets;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang.CharUtils;
@@ -26,22 +32,15 @@ import org.sonar.api.server.ServerSide;
 import org.sonar.core.platform.PluginInfo;
 import org.sonar.core.platform.PluginRepository;
 import org.sonar.core.platform.RemotePlugin;
-import org.sonar.server.platform.DefaultServerFileSystem;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.io.Writer;
-import java.nio.charset.StandardCharsets;
+import org.sonar.server.platform.ServerFileSystem;
 
 @ServerSide
 public final class GeneratePluginIndex {
 
-  private DefaultServerFileSystem fileSystem;
-  private PluginRepository repository;
+  private final ServerFileSystem fileSystem;
+  private final PluginRepository repository;
 
-  public GeneratePluginIndex(DefaultServerFileSystem fileSystem, PluginRepository repository) {
+  public GeneratePluginIndex(ServerFileSystem fileSystem, PluginRepository repository) {
     this.fileSystem = fileSystem;
     this.repository = repository;
   }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/startup/RegisterServletFilters.java b/server/sonar-server/src/main/java/org/sonar/server/startup/RegisterServletFilters.java
deleted file mode 100644 (file)
index 160adfa..0000000
+++ /dev/null
@@ -1,52 +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.server.startup;
-
-import org.sonar.api.web.ServletFilter;
-import org.sonar.server.platform.MasterServletFilter;
-
-import javax.servlet.ServletException;
-
-import java.util.Arrays;
-
-/**
- * @since 3.5
- */
-public class RegisterServletFilters {
-  private final ServletFilter[] filters;
-
-  public RegisterServletFilters(ServletFilter[] filters) {
-    this.filters = filters;
-  }
-
-  public RegisterServletFilters() {
-    this(new ServletFilter[0]);
-  }
-
-  public void start() throws ServletException {
-    if (MasterServletFilter.INSTANCE != null) {
-      // Probably a database upgrade. MasterSlaveFilter was instantiated by the servlet container
-      // while picocontainer was not completely up.
-      // See https://jira.sonarsource.com/browse/SONAR-3612
-      MasterServletFilter.INSTANCE.initFilters(Arrays.asList(filters));
-    }
-  }
-}
-
index 7bdcb3f5d6174b23c9b8a4a7b8007a278b567fd2..da4cce1c24a63ecefa7af733d16bc28deab50abd 100644 (file)
 
 package org.sonar.server.updatecenter.ws;
 
-import static com.google.common.base.Preconditions.checkArgument;
-import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
-import static org.apache.commons.io.IOUtils.closeQuietly;
-import static org.sonar.api.server.ws.Request.Part;
-
 import java.io.File;
 import java.io.InputStream;
 import java.nio.file.Files;
@@ -32,9 +27,14 @@ import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.core.permission.GlobalPermissions;
-import org.sonar.server.platform.DefaultServerFileSystem;
+import org.sonar.server.platform.ServerFileSystem;
 import org.sonar.server.user.UserSession;
 
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+import static org.apache.commons.io.IOUtils.closeQuietly;
+import static org.sonar.api.server.ws.Request.Part;
+
 public class UploadAction implements UpdateCenterWsAction {
 
   public static final String PARAM_FILE = "file";
@@ -42,7 +42,7 @@ public class UploadAction implements UpdateCenterWsAction {
   private final UserSession userSession;
   private final File downloadDir;
 
-  public UploadAction(UserSession userSession, DefaultServerFileSystem fileSystem) {
+  public UploadAction(UserSession userSession, ServerFileSystem fileSystem) {
     this.userSession = userSession;
     this.downloadDir = fileSystem.getDownloadedPluginsDir();
   }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/util/ClassLoaderUtils.java b/server/sonar-server/src/main/java/org/sonar/server/util/ClassLoaderUtils.java
new file mode 100644 (file)
index 0000000..68b5bb8
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * 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.server.util;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.base.Throwables;
+import com.google.common.collect.Lists;
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import javax.annotation.Nullable;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.CharEncoding;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.utils.log.Loggers;
+
+/**
+ * @since 3.0
+ */
+public class ClassLoaderUtils {
+
+  private ClassLoaderUtils() {
+    // only static methods
+  }
+
+  public static File copyResources(ClassLoader classLoader, String rootPath, File toDir, Function<String, String> relocationFunction) {
+    Collection<String> relativePaths = listFiles(classLoader, rootPath);
+    for (String relativePath : relativePaths) {
+      URL resource = classLoader.getResource(relativePath);
+      String filename = relocationFunction.apply(relativePath);
+      File toFile = new File(toDir, filename);
+      try {
+        FileUtils.copyURLToFile(resource, toFile);
+      } catch (IOException e) {
+        throw new IllegalStateException("Fail to extract " + relativePath + " to " + toFile.getAbsolutePath(), e);
+      }
+    }
+
+    return toDir;
+  }
+
+  /**
+   * Finds files within a given directory and its subdirectories
+   *
+   * @param classLoader
+   * @param rootPath    the root directory, for example org/sonar/sqale
+   * @return a list of relative paths, for example {"org/sonar/sqale/foo/bar.txt}. Never null.
+   */
+  public static Collection<String> listFiles(ClassLoader classLoader, String rootPath) {
+    return listResources(classLoader, rootPath, path -> !StringUtils.endsWith(path, "/"));
+  }
+
+  /**
+   * Finds directories and files within a given directory and its subdirectories.
+   *
+   * @param classLoader
+   * @param rootPath    the root directory, for example org/sonar/sqale, or a file in this root directory, for example org/sonar/sqale/index.txt
+   * @param predicate
+   * @return a list of relative paths, for example {"org/sonar/sqale", "org/sonar/sqale/foo", "org/sonar/sqale/foo/bar.txt}. Never null.
+   */
+  public static Collection<String> listResources(ClassLoader classLoader, String rootPath, Predicate<String> predicate) {
+    String jarPath = null;
+    JarFile jar = null;
+    try {
+      Collection<String> paths = Lists.newArrayList();
+      URL root = classLoader.getResource(rootPath);
+      if (root != null) {
+        checkJarFile(root);
+
+        // Path of the root directory
+        // Examples :
+        // org/sonar/sqale/index.txt  -> rootDirectory is org/sonar/sqale
+        // org/sonar/sqale/  -> rootDirectory is org/sonar/sqale
+        // org/sonar/sqale  -> rootDirectory is org/sonar/sqale
+        String rootDirectory = rootPath;
+        if (StringUtils.substringAfterLast(rootPath, "/").indexOf('.') >= 0) {
+          rootDirectory = StringUtils.substringBeforeLast(rootPath, "/");
+        }
+        //strip out only the JAR file
+        jarPath = root.getPath().substring(5, root.getPath().indexOf('!'));
+        jar = new JarFile(URLDecoder.decode(jarPath, CharEncoding.UTF_8));
+        Enumeration<JarEntry> entries = jar.entries();
+        while (entries.hasMoreElements()) {
+          String name = entries.nextElement().getName();
+          if (name.startsWith(rootDirectory) && predicate.apply(name)) {
+            paths.add(name);
+          }
+        }
+      }
+      return paths;
+    } catch (Exception e) {
+      throw Throwables.propagate(e);
+    } finally {
+      closeJar(jar, jarPath);
+    }
+  }
+
+  private static void closeJar(@Nullable JarFile jar, String jarPath) {
+    if (jar != null) {
+      try {
+        jar.close();
+      } catch (Exception e) {
+        Loggers.get(ClassLoaderUtils.class).error("Fail to close JAR file: " + jarPath, e);
+      }
+    }
+  }
+
+  private static void checkJarFile(URL root) {
+    if (!"jar".equals(root.getProtocol())) {
+      throw new IllegalStateException("Unsupported protocol: " + root.getProtocol());
+    }
+  }
+}
index 7baaf99538b4fcb79e9cd94ce70998c2467eb6d5..9f73068a1c565779dab329c3f3efec3f8ae78ce6 100644 (file)
@@ -19,6 +19,8 @@
  */
 package org.sonar.server.batch;
 
+import java.io.File;
+import java.io.IOException;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.lang.CharUtils;
 import org.junit.Before;
@@ -26,10 +28,7 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.junit.rules.TemporaryFolder;
-import org.sonar.api.platform.Server;
-
-import java.io.File;
-import java.io.IOException;
+import org.sonar.server.platform.ServerFileSystem;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
@@ -43,16 +42,16 @@ public class BatchIndexTest {
   @Rule
   public ExpectedException thrown = ExpectedException.none();
 
-  File jar;
+  private File jar;
 
-  Server server = mock(Server.class);
+  private ServerFileSystem fs = mock(ServerFileSystem.class);
 
   @Before
   public void prepare_fs() throws IOException {
-    File rootDir = temp.newFolder();
-    when(server.getRootDir()).thenReturn(rootDir);
+    File homeDir = temp.newFolder();
+    when(fs.getHomeDir()).thenReturn(homeDir);
 
-    File batchDir = new File(rootDir, "lib/batch");
+    File batchDir = new File(homeDir, "lib/batch");
     FileUtils.forceMkdir(batchDir);
     jar = new File(batchDir, "sonar-batch.jar");
     FileUtils.writeStringToFile(new File(batchDir, "sonar-batch.jar"), "foo");
@@ -60,7 +59,7 @@ public class BatchIndexTest {
 
   @Test
   public void get_index() {
-    BatchIndex batchIndex = new BatchIndex(server);
+    BatchIndex batchIndex = new BatchIndex(fs);
     batchIndex.start();
 
     String index = batchIndex.getIndex();
@@ -71,7 +70,7 @@ public class BatchIndexTest {
 
   @Test
   public void get_file() {
-    BatchIndex batchIndex = new BatchIndex(server);
+    BatchIndex batchIndex = new BatchIndex(fs);
     batchIndex.start();
 
     File file = batchIndex.getFile("sonar-batch.jar");
@@ -87,7 +86,7 @@ public class BatchIndexTest {
     thrown.expect(IllegalArgumentException.class);
     thrown.expectMessage("Bad filename: ../sonar-batch.jar");
 
-    BatchIndex batchIndex = new BatchIndex(server);
+    BatchIndex batchIndex = new BatchIndex(fs);
     batchIndex.start();
 
     batchIndex.getFile("../sonar-batch.jar");
@@ -98,7 +97,7 @@ public class BatchIndexTest {
     thrown.expect(IllegalArgumentException.class);
     thrown.expectMessage("Bad filename: other.jar");
 
-    BatchIndex batchIndex = new BatchIndex(server);
+    BatchIndex batchIndex = new BatchIndex(fs);
     batchIndex.start();
 
     batchIndex.getFile("other.jar");
index 3b957fddc2f86632331babf982b8f0d37643f9f7..66f2191f6c344ed95f4f539516d93974a33fc319 100644 (file)
@@ -27,7 +27,6 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.sonar.api.config.Settings;
-import org.sonar.api.platform.Server;
 import org.sonar.api.security.DefaultGroups;
 import org.sonar.api.utils.System2;
 import org.sonar.api.web.UserRole;
@@ -46,6 +45,7 @@ import org.sonar.server.issue.index.IssueDoc;
 import org.sonar.server.issue.index.IssueIndex;
 import org.sonar.server.issue.index.IssueIndexDefinition;
 import org.sonar.server.issue.index.IssueIndexer;
+import org.sonar.server.platform.ServerFileSystem;
 import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.ws.WsTester;
 
@@ -76,9 +76,10 @@ public class IssuesActionTest {
   @Rule
   public UserSessionRule userSessionRule = UserSessionRule.standalone();
 
-  IssueIndex issueIndex;
-  IssueIndexer issueIndexer;
-  IssueAuthorizationIndexer issueAuthorizationIndexer;
+  private IssueIndex issueIndex;
+  private IssueIndexer issueIndexer;
+  private IssueAuthorizationIndexer issueAuthorizationIndexer;
+  private ServerFileSystem fs = mock(ServerFileSystem.class);
 
   WsTester tester;
 
@@ -91,7 +92,7 @@ public class IssuesActionTest {
     issueAuthorizationIndexer = new IssueAuthorizationIndexer(null, es.client());
     issuesAction = new IssuesAction(db.getDbClient(), issueIndex, userSessionRule, new ComponentFinder(db.getDbClient()));
 
-    tester = new WsTester(new BatchWs(new BatchIndex(mock(Server.class)), issuesAction));
+    tester = new WsTester(new BatchWs(new BatchIndex(fs), issuesAction));
   }
 
   @Test
index f877b0e9d69238423835c75b690e6fdd8dacfbd4..30a96caffe2f8030f075901dd62a6fdf68fec3fd 100644 (file)
@@ -27,10 +27,10 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.sonar.api.config.Settings;
-import org.sonar.api.platform.Server;
 import org.sonar.scanner.protocol.input.ScannerInput.User;
 import org.sonar.server.es.EsTester;
 import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.platform.ServerFileSystem;
 import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.user.index.UserDoc;
 import org.sonar.server.user.index.UserIndex;
@@ -61,7 +61,7 @@ public class UsersActionTest {
   public void before() {
     userIndex = new UserIndex(es.client());
     usersAction = new UsersAction(userIndex, userSessionRule);
-    tester = new WsTester(new BatchWs(new BatchIndex(mock(Server.class)), usersAction));
+    tester = new WsTester(new BatchWs(new BatchIndex(mock(ServerFileSystem.class)), usersAction));
   }
 
   @Test
index af1e1093aff3bf646000f978c312312ef3fc05b5..c19c7274acefb53fd57cbd3ac744bc2a9f5004bb 100644 (file)
@@ -38,6 +38,7 @@ import java.io.IOException;
 import java.net.URL;
 import java.net.URLClassLoader;
 import java.util.Collection;
+import org.sonar.server.util.ClassLoaderUtils;
 
 import static org.junit.Assert.assertThat;
 import static org.junit.matchers.JUnitMatchers.hasItems;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/MasterServletFilterTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/MasterServletFilterTest.java
deleted file mode 100644 (file)
index 157879f..0000000
+++ /dev/null
@@ -1,143 +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.server.platform;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.api.web.ServletFilter;
-
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collections;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-public class MasterServletFilterTest {
-
-  @Rule
-  public ExpectedException thrown = ExpectedException.none();
-
-  @Before
-  public void resetSingleton() {
-    MasterServletFilter.INSTANCE = null;
-  }
-
-  @Test
-  public void should_init_and_destroy_filters() throws Exception {
-    ServletFilter filter = mock(ServletFilter.class);
-    FilterConfig config = mock(FilterConfig.class);
-    MasterServletFilter master = new MasterServletFilter();
-    master.init(config, Arrays.asList(filter));
-
-    assertThat(master.getFilters()).containsOnly(filter);
-    verify(filter).init(config);
-
-    master.destroy();
-    verify(filter).destroy();
-  }
-
-  @Test
-  public void servlet_container_should_instantiate_only_a_single_master_instance() {
-    new MasterServletFilter();
-
-    thrown.expect(IllegalStateException.class);
-    thrown.expectMessage("Servlet filter org.sonar.server.platform.MasterServletFilter is already instantiated");
-    new MasterServletFilter();
-  }
-
-  @Test
-  public void should_propagate_initialization_failure() throws Exception {
-    thrown.expect(IllegalStateException.class);
-    thrown.expectMessage("foo");
-
-    ServletFilter filter = mock(ServletFilter.class);
-    doThrow(new IllegalStateException("foo")).when(filter).init(any(FilterConfig.class));
-
-    FilterConfig config = mock(FilterConfig.class);
-    MasterServletFilter filters = new MasterServletFilter();
-    filters.init(config, Arrays.asList(filter));
-  }
-
-  @Test
-  public void filters_should_be_optional() throws Exception {
-    FilterConfig config = mock(FilterConfig.class);
-    MasterServletFilter filters = new MasterServletFilter();
-    filters.init(config, Collections.<ServletFilter>emptyList());
-
-    ServletRequest request = mock(HttpServletRequest.class);
-    ServletResponse response = mock(HttpServletResponse.class);
-    FilterChain chain = mock(FilterChain.class);
-    filters.doFilter(request, response, chain);
-
-    verify(chain).doFilter(request, response);
-  }
-
-  @Test
-  public void should_keep_filter_ordering() throws Exception {
-    TrueFilter filter1 = new TrueFilter();
-    TrueFilter filter2 = new TrueFilter();
-
-    MasterServletFilter filters = new MasterServletFilter();
-    filters.init(mock(FilterConfig.class), Arrays.<ServletFilter>asList(filter1, filter2));
-
-    HttpServletRequest request = mock(HttpServletRequest.class);
-    when(request.getRequestURI()).thenReturn("/foo/bar");
-    when(request.getContextPath()).thenReturn("");
-    ServletResponse response = mock(HttpServletResponse.class);
-    FilterChain chain = mock(FilterChain.class);
-    filters.doFilter(request, response, chain);
-
-    assertThat(filter1.count).isEqualTo(1);
-    assertThat(filter2.count).isEqualTo(2);
-  }
-
-  private static final class TrueFilter extends ServletFilter {
-    private static int globalCount = 0;
-    private int count = 0;
-
-    public void init(FilterConfig filterConfig) throws ServletException {
-    }
-
-    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
-      globalCount++;
-      count = globalCount;
-      filterChain.doFilter(servletRequest, servletResponse);
-    }
-
-    public void destroy() {
-    }
-
-  }
-}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/ProfilingFilterTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/ProfilingFilterTest.java
deleted file mode 100644 (file)
index dc27813..0000000
+++ /dev/null
@@ -1,105 +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.server.platform;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mockito;
-
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class ProfilingFilterTest {
-
-  private ProfilingFilter filter;
-  private FilterChain chain;
-
-  @Before
-  public void initialize() throws Exception {
-    FilterConfig filterConfig = mock(FilterConfig.class);
-    when(filterConfig.getInitParameter("staticDirs")).thenReturn("/static,/assets");
-    ServletContext context = mock(ServletContext.class);
-    when(context.getContextPath()).thenReturn("/context");
-    when(filterConfig.getServletContext()).thenReturn(context);
-    chain = mock(FilterChain.class);
-
-    filter = new ProfilingFilter();
-    filter.init(filterConfig);
-  }
-
-  @Test
-  public void should_profile_service_call() throws Exception {
-    filter.doFilter(request("POST", "/context/service/call", "param=value"), null, chain);
-  }
-
-  @Test
-  public void should_profile_service() throws Exception {
-    filter.doFilter(request("POST", "/context/service", null), null, chain);
-  }
-
-  @Test
-  public void should_profile_context_root_as_slash2() throws Exception {
-    filter.doFilter(request("POST", "/context", null), null, chain);
-  }
-
-  @Test(expected = ServletException.class)
-  public void should_profile_even_when_exception() throws Exception {
-    Mockito.doThrow(new ServletException()).when(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));
-    filter.doFilter(request("POST", "/context/service/call", "param=value"), null, chain);
-  }
-
-  @Test
-  public void should_not_profile_non_http_request() throws Exception {
-    filter.doFilter(mock(ServletRequest.class), null, chain);
-  }
-
-  @Test
-  public void should_not_profile_static_resource() throws Exception {
-    filter.doFilter(request("GET", "/context/static/image.png", null), null, chain);
-  }
-
-  @Test
-  public void should_profile_static_resource_if_no_config() throws Exception {
-    FilterConfig filterConfig = mock(FilterConfig.class);
-    ServletContext context = mock(ServletContext.class);
-    when(context.getContextPath()).thenReturn("/context");
-    when(filterConfig.getServletContext()).thenReturn(context);
-
-    filter.init(filterConfig);
-    filter.doFilter(request("GET", "/context/static/image.png", null), null, chain);
-  }
-
-  private HttpServletRequest request(String method, String path, String query) {
-    HttpServletRequest request = mock(HttpServletRequest.class);
-    when(request.getMethod()).thenReturn(method);
-    when(request.getRequestURI()).thenReturn(path);
-    when(request.getQueryString()).thenReturn(query);
-    return request;
-  }
-}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/RailsAppsDeployerTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/RailsAppsDeployerTest.java
deleted file mode 100644 (file)
index a725233..0000000
+++ /dev/null
@@ -1,118 +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.server.platform;
-
-import org.apache.commons.io.FileUtils;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.api.platform.ServerFileSystem;
-import org.sonar.core.platform.PluginInfo;
-import org.sonar.core.platform.PluginRepository;
-
-import java.io.File;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.Collections;
-
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class RailsAppsDeployerTest {
-
-  @Rule
-  public TemporaryFolder temp = new TemporaryFolder();
-
-  @Test
-  public void hasRubyRailsApp() throws Exception {
-    ClassLoader classLoader = new URLClassLoader(new URL[]{
-      getClass().getResource("/org/sonar/server/platform/RailsAppsDeployerTest/FakeRubyRailsApp.jar").toURI().toURL()}, null);
-
-    assertTrue(RailsAppsDeployer.hasRailsApp("fake", classLoader));
-    assertFalse(RailsAppsDeployer.hasRailsApp("other", classLoader));
-  }
-
-  @Test
-  public void deployRubyRailsApp() throws Exception {
-    File tempDir = this.temp.getRoot();
-    ClassLoader classLoader = new URLClassLoader(new URL[]{
-      getClass().getResource("/org/sonar/server/platform/RailsAppsDeployerTest/FakeRubyRailsApp.jar").toURI().toURL()}, null);
-
-    RailsAppsDeployer.deployRailsApp(tempDir, "fake", classLoader);
-
-    File appDir = new File(tempDir, "fake");
-    assertThat(appDir.isDirectory(), is(true));
-    assertThat(appDir.exists(), is(true));
-    assertThat(FileUtils.listFiles(appDir, null, true).size(), is(3));
-    assertThat(new File(appDir, "init.rb").exists(), is(true));
-    assertThat(new File(appDir, "app/controllers/fake_controller.rb").exists(), is(true));
-    assertThat(new File(appDir, "app/views/fake/index.html.erb").exists(), is(true));
-  }
-
-  @Test
-  public void deployRubyRailsApps_no_apps() {
-    ServerFileSystem fileSystem = mock(ServerFileSystem.class);
-    File tempDir = this.temp.getRoot();
-    when(fileSystem.getTempDir()).thenReturn(tempDir);
-
-    PluginRepository pluginRepository = mock(PluginRepository.class);
-    when(pluginRepository.getPluginInfos()).thenReturn(Collections.<PluginInfo>emptyList());
-    new RailsAppsDeployer(fileSystem, pluginRepository).start();
-
-    File appDir = new File(tempDir, "ror");
-    assertThat(appDir.isDirectory(), is(true));
-    assertThat(appDir.exists(), is(true));
-    assertThat(FileUtils.listFiles(appDir, null, true).size(), is(0));
-  }
-
-  @Test
-  public void prepareRubyRailsRootDirectory() throws Exception {
-    ServerFileSystem fileSystem = mock(ServerFileSystem.class);
-    File tempDir = this.temp.getRoot();
-    when(fileSystem.getTempDir()).thenReturn(tempDir);
-
-    File dir = new RailsAppsDeployer(fileSystem, mock(PluginRepository.class)).prepareRailsDirectory();
-
-    assertThat(dir.isDirectory(), is(true));
-    assertThat(dir.exists(), is(true));
-    assertThat(dir.getCanonicalPath(), is(new File(tempDir, "ror").getCanonicalPath()));
-  }
-
-  @Test
-  public void prepareRubyRailsRootDirectory_delete_existing_dir() throws Exception {
-    ServerFileSystem fileSystem = mock(ServerFileSystem.class);
-    File tempDir = this.temp.getRoot();
-    when(fileSystem.getTempDir()).thenReturn(tempDir);
-
-    File file = new File(tempDir, "ror/foo/bar.txt");
-    FileUtils.writeStringToFile(file, "foooo");
-
-    File dir = new RailsAppsDeployer(fileSystem, mock(PluginRepository.class)).prepareRailsDirectory();
-
-    assertThat(dir.isDirectory(), is(true));
-    assertThat(dir.exists(), is(true));
-    assertThat(dir.getCanonicalPath(), is(new File(tempDir, "ror").getCanonicalPath()));
-    assertThat(FileUtils.listFiles(new File(tempDir, "ror"), null, true).size(), is(0));
-  }
-}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/RoutesFilterTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/RoutesFilterTest.java
deleted file mode 100644 (file)
index a9c4630..0000000
+++ /dev/null
@@ -1,87 +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.server.platform;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-
-import javax.servlet.FilterChain;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.junit.Before;
-import org.junit.Test;
-
-public class RoutesFilterTest {
-
-  HttpServletRequest request = mock(HttpServletRequest.class);
-  HttpServletResponse response = mock(HttpServletResponse.class);
-  FilterChain chain = mock(FilterChain.class);
-
-  RoutesFilter underTest = new RoutesFilter();
-
-  @Before
-  public void setUp() throws Exception {
-    when(request.getContextPath()).thenReturn("/sonarqube");
-  }
-
-  @Test
-  public void send_redirect_when_url_contains_batch_with_jar() throws Exception {
-    when(request.getRequestURI()).thenReturn("/batch/file.jar");
-
-    underTest.doFilter(request, response, chain);
-
-    verify(response).sendRedirect("/sonarqube/batch/file?name=file.jar");
-    verifyZeroInteractions(chain);
-  }
-
-  @Test
-  public void send_redirect_when_url_contains_batch_bootstrap() throws Exception {
-    when(request.getRequestURI()).thenReturn("/batch_bootstrap/index");
-
-    underTest.doFilter(request, response, chain);
-
-    verify(response).sendRedirect("/sonarqube/batch/index");
-    verifyZeroInteractions(chain);
-  }
-
-  @Test
-  public void send_redirect_when_url_contains_api_sources() throws Exception {
-    when(request.getRequestURI()).thenReturn("/api/sources");
-    when(request.getQueryString()).thenReturn("resource=my.project");
-
-    underTest.doFilter(request, response, chain);
-
-    verify(response).sendRedirect("/sonarqube/api/sources/index?resource=my.project");
-    verifyZeroInteractions(chain);
-  }
-
-  @Test
-  public void does_not_redirect_and_execute_remaining_filter_on_unknown_path() throws Exception {
-    when(request.getRequestURI()).thenReturn("/api/issues/search");
-
-    underTest.doFilter(request, response, chain);
-
-    verify(chain).doFilter(request, response);
-    verifyZeroInteractions(response);
-  }
-}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/RubyRailsContextListenerTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/RubyRailsContextListenerTest.java
deleted file mode 100644 (file)
index 976d7c7..0000000
+++ /dev/null
@@ -1,72 +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.server.platform;
-
-import java.io.IOException;
-import javax.servlet.ServletContextEvent;
-import org.jruby.rack.RackApplicationFactory;
-import org.jruby.rack.servlet.ServletRackContext;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.mockito.Mockito;
-
-import static java.lang.Boolean.TRUE;
-import static org.mockito.Matchers.anyObject;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-public class RubyRailsContextListenerTest {
-
-  @Rule
-  public ExpectedException expectedException = ExpectedException.none();
-
-  ServletContextEvent event = mock(ServletContextEvent.class, Mockito.RETURNS_DEEP_STUBS);
-  RubyRailsContextListener underTest = new RubyRailsContextListener();
-
-  @Test
-  public void do_not_initialize_rails_if_error_during_startup() {
-    when(event.getServletContext().getAttribute(PlatformServletContextListener.STARTED_ATTRIBUTE)).thenReturn(null);
-
-    underTest.contextInitialized(event);
-
-    verify(event.getServletContext(), never()).setAttribute(anyString(), anyObject());
-  }
-
-  @Test
-  public void initialize_rails_if_no_errors_during_startup() {
-    when(event.getServletContext().getAttribute(PlatformServletContextListener.STARTED_ATTRIBUTE)).thenReturn(TRUE);
-    underTest.contextInitialized(event);
-    // Ruby environment is started
-    // See RailsServletContextListener -> RackServletContextListener
-    verify(event.getServletContext()).setAttribute(eq("rack.factory"), anyObject());
-  }
-
-  @Test
-  public void always_propagates_initialization_errors() {
-    expectedException.expect(RuntimeException.class);
-
-    underTest.handleInitializationException(new IOException(), mock(RackApplicationFactory.class), mock(ServletRackContext.class));
-  }
-}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/SecurityServletFilterTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/SecurityServletFilterTest.java
deleted file mode 100644 (file)
index ce61a5a..0000000
+++ /dev/null
@@ -1,103 +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.server.platform;
-
-import java.io.IOException;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.junit.Test;
-
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.startsWith;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-public class SecurityServletFilterTest {
-
-  SecurityServletFilter underTest = new SecurityServletFilter();
-  HttpServletResponse response = mock(HttpServletResponse.class);
-  FilterChain chain = mock(FilterChain.class);
-
-  @Test
-  public void allow_GET_method() throws IOException, ServletException {
-    assertThatMethodIsAllowed("GET");
-  }
-
-  @Test
-  public void allow_HEAD_method() throws IOException, ServletException {
-    assertThatMethodIsAllowed("HEAD");
-  }
-
-  @Test
-  public void allow_PUT_method() throws IOException, ServletException {
-    assertThatMethodIsAllowed("PUT");
-  }
-
-  @Test
-  public void allow_POST_method() throws IOException, ServletException {
-    assertThatMethodIsAllowed("POST");
-  }
-
-  private void assertThatMethodIsAllowed(String httpMethod) throws IOException, ServletException {
-    HttpServletRequest request = newRequest(httpMethod);
-    underTest.doFilter(request, response, chain);
-    verify(response, never()).setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
-    verify(chain).doFilter(request, response);
-  }
-
-  @Test
-  public void deny_OPTIONS_method() throws IOException, ServletException {
-    assertThatMethodIsDenied("OPTIONS");
-  }
-
-  @Test
-  public void deny_TRACE_method() throws IOException, ServletException {
-    assertThatMethodIsDenied("TRACE");
-  }
-
-  private void assertThatMethodIsDenied(String httpMethod) throws IOException, ServletException {
-    underTest.doFilter(newRequest(httpMethod), response, chain);
-    verify(response).setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
-  }
-
-  @Test
-  public void set_secured_headers() throws ServletException, IOException {
-    underTest.init(mock(FilterConfig.class));
-    HttpServletRequest request = newRequest("GET");
-
-    underTest.doFilter(request, response, chain);
-
-    verify(response, times(3)).addHeader(startsWith("X-"), anyString());
-
-    underTest.destroy();
-  }
-
-  private HttpServletRequest newRequest(String httpMethod) {
-    HttpServletRequest req = mock(HttpServletRequest.class);
-    when(req.getMethod()).thenReturn(httpMethod);
-    return req;
-  }
-}
index eb9777bd0c779b12580a850ad10dd97217cfa6cd..2abcb52ae328d1f9f49aca3a91980ab785bd2ef3 100644 (file)
  */
 package org.sonar.server.platform;
 
+import java.io.File;
 import org.apache.commons.io.FileUtils;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.junit.rules.TemporaryFolder;
-import org.sonar.api.platform.ServerFileSystem;
 import org.sonar.api.utils.TempFolder;
 
-import java.io.File;
-
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
index b4a6a69ce7a02df0482a5f00aa1c3a4909216ee9..a0378c3a4527be57b5413af5cda626855fbf12c8 100644 (file)
@@ -1,3 +1,22 @@
+/*
+ * 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.server.platform.cluster;
 
 import org.junit.Rule;
index f3d9b98ffb855946c6b624d06de1e209c7404929..eaa0db824dffae6044e2d36d9300b3ad64795a3b 100644 (file)
@@ -1,3 +1,22 @@
+/*
+ * 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.server.platform.cluster;
 
 public class ClusterMock implements Cluster {
diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/web/MasterServletFilterTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/web/MasterServletFilterTest.java
new file mode 100644 (file)
index 0000000..255dab8
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * 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.server.platform.web;
+
+import java.io.IOException;
+import java.util.Collections;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.web.ServletFilter;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class MasterServletFilterTest {
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  @Before
+  public void resetSingleton() {
+    MasterServletFilter.INSTANCE = null;
+  }
+
+  @Test
+  public void should_init_and_destroy_filters() throws Exception {
+    ServletFilter filter = mock(ServletFilter.class);
+    FilterConfig config = mock(FilterConfig.class);
+    MasterServletFilter master = new MasterServletFilter();
+    master.init(config, singletonList(filter));
+
+    assertThat(master.getFilters()).containsOnly(filter);
+    verify(filter).init(config);
+
+    master.destroy();
+    verify(filter).destroy();
+  }
+
+  @Test
+  public void servlet_container_should_instantiate_only_a_single_master_instance() {
+    new MasterServletFilter();
+
+    thrown.expect(IllegalStateException.class);
+    thrown.expectMessage("Servlet filter org.sonar.server.platform.web.MasterServletFilter is already instantiated");
+    new MasterServletFilter();
+  }
+
+  @Test
+  public void should_propagate_initialization_failure() throws Exception {
+    thrown.expect(IllegalStateException.class);
+    thrown.expectMessage("foo");
+
+    ServletFilter filter = mock(ServletFilter.class);
+    doThrow(new IllegalStateException("foo")).when(filter).init(any(FilterConfig.class));
+
+    FilterConfig config = mock(FilterConfig.class);
+    MasterServletFilter filters = new MasterServletFilter();
+    filters.init(config, singletonList(filter));
+  }
+
+  @Test
+  public void filters_should_be_optional() throws Exception {
+    FilterConfig config = mock(FilterConfig.class);
+    MasterServletFilter filters = new MasterServletFilter();
+    filters.init(config, Collections.emptyList());
+
+    ServletRequest request = mock(HttpServletRequest.class);
+    ServletResponse response = mock(HttpServletResponse.class);
+    FilterChain chain = mock(FilterChain.class);
+    filters.doFilter(request, response, chain);
+
+    verify(chain).doFilter(request, response);
+  }
+
+  @Test
+  public void should_keep_filter_ordering() throws Exception {
+    TrueFilter filter1 = new TrueFilter();
+    TrueFilter filter2 = new TrueFilter();
+
+    MasterServletFilter filters = new MasterServletFilter();
+    filters.init(mock(FilterConfig.class), asList(filter1, filter2));
+
+    HttpServletRequest request = mock(HttpServletRequest.class);
+    when(request.getRequestURI()).thenReturn("/foo/bar");
+    when(request.getContextPath()).thenReturn("");
+    ServletResponse response = mock(HttpServletResponse.class);
+    FilterChain chain = mock(FilterChain.class);
+    filters.doFilter(request, response, chain);
+
+    assertThat(filter1.count).isEqualTo(1);
+    assertThat(filter2.count).isEqualTo(2);
+  }
+
+  private static final class TrueFilter extends ServletFilter {
+    private static int globalCount = 0;
+    private int count = 0;
+
+    public void init(FilterConfig filterConfig) throws ServletException {
+    }
+
+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+      globalCount++;
+      count = globalCount;
+      filterChain.doFilter(servletRequest, servletResponse);
+    }
+
+    public void destroy() {
+    }
+
+  }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/web/ProfilingFilterTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/web/ProfilingFilterTest.java
new file mode 100644 (file)
index 0000000..93c339c
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * 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.server.platform.web;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import org.sonar.server.platform.web.ProfilingFilter;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ProfilingFilterTest {
+
+  private ProfilingFilter filter;
+  private FilterChain chain;
+
+  @Before
+  public void initialize() throws Exception {
+    FilterConfig filterConfig = mock(FilterConfig.class);
+    when(filterConfig.getInitParameter("staticDirs")).thenReturn("/static,/assets");
+    ServletContext context = mock(ServletContext.class);
+    when(context.getContextPath()).thenReturn("/context");
+    when(filterConfig.getServletContext()).thenReturn(context);
+    chain = mock(FilterChain.class);
+
+    filter = new ProfilingFilter();
+    filter.init(filterConfig);
+  }
+
+  @Test
+  public void should_profile_service_call() throws Exception {
+    filter.doFilter(request("POST", "/context/service/call", "param=value"), null, chain);
+  }
+
+  @Test
+  public void should_profile_service() throws Exception {
+    filter.doFilter(request("POST", "/context/service", null), null, chain);
+  }
+
+  @Test
+  public void should_profile_context_root_as_slash2() throws Exception {
+    filter.doFilter(request("POST", "/context", null), null, chain);
+  }
+
+  @Test(expected = ServletException.class)
+  public void should_profile_even_when_exception() throws Exception {
+    Mockito.doThrow(new ServletException()).when(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));
+    filter.doFilter(request("POST", "/context/service/call", "param=value"), null, chain);
+  }
+
+  @Test
+  public void should_not_profile_non_http_request() throws Exception {
+    filter.doFilter(mock(ServletRequest.class), null, chain);
+  }
+
+  @Test
+  public void should_not_profile_static_resource() throws Exception {
+    filter.doFilter(request("GET", "/context/static/image.png", null), null, chain);
+  }
+
+  @Test
+  public void should_profile_static_resource_if_no_config() throws Exception {
+    FilterConfig filterConfig = mock(FilterConfig.class);
+    ServletContext context = mock(ServletContext.class);
+    when(context.getContextPath()).thenReturn("/context");
+    when(filterConfig.getServletContext()).thenReturn(context);
+
+    filter.init(filterConfig);
+    filter.doFilter(request("GET", "/context/static/image.png", null), null, chain);
+  }
+
+  private HttpServletRequest request(String method, String path, String query) {
+    HttpServletRequest request = mock(HttpServletRequest.class);
+    when(request.getMethod()).thenReturn(method);
+    when(request.getRequestURI()).thenReturn(path);
+    when(request.getQueryString()).thenReturn(query);
+    return request;
+  }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/web/RailsAppsDeployerTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/web/RailsAppsDeployerTest.java
new file mode 100644 (file)
index 0000000..937d5e8
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * 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.server.platform.web;
+
+import java.io.File;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Collections;
+import org.apache.commons.io.FileUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.core.platform.PluginRepository;
+import org.sonar.server.platform.ServerFileSystem;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class RailsAppsDeployerTest {
+
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
+  @Test
+  public void hasRubyRailsApp() throws Exception {
+    ClassLoader classLoader = new URLClassLoader(new URL[]{
+      getClass().getResource("/org/sonar/server/platform/web/RailsAppsDeployerTest/FakeRubyRailsApp.jar").toURI().toURL()}, null);
+
+    assertTrue(RailsAppsDeployer.hasRailsApp("fake", classLoader));
+    assertFalse(RailsAppsDeployer.hasRailsApp("other", classLoader));
+  }
+
+  @Test
+  public void deployRubyRailsApp() throws Exception {
+    File tempDir = this.temp.getRoot();
+    ClassLoader classLoader = new URLClassLoader(new URL[]{
+      getClass().getResource("/org/sonar/server/platform/web/RailsAppsDeployerTest/FakeRubyRailsApp.jar").toURI().toURL()}, null);
+
+    RailsAppsDeployer.deployRailsApp(tempDir, "fake", classLoader);
+
+    File appDir = new File(tempDir, "fake");
+    assertThat(appDir.isDirectory(), is(true));
+    assertThat(appDir.exists(), is(true));
+    assertThat(FileUtils.listFiles(appDir, null, true).size(), is(3));
+    assertThat(new File(appDir, "init.rb").exists(), is(true));
+    assertThat(new File(appDir, "app/controllers/fake_controller.rb").exists(), is(true));
+    assertThat(new File(appDir, "app/views/fake/index.html.erb").exists(), is(true));
+  }
+
+  @Test
+  public void deployRubyRailsApps_no_apps() {
+    ServerFileSystem fileSystem = mock(ServerFileSystem.class);
+    File tempDir = this.temp.getRoot();
+    when(fileSystem.getTempDir()).thenReturn(tempDir);
+
+    PluginRepository pluginRepository = mock(PluginRepository.class);
+    when(pluginRepository.getPluginInfos()).thenReturn(Collections.emptyList());
+    new RailsAppsDeployer(fileSystem, pluginRepository).start();
+
+    File appDir = new File(tempDir, "ror");
+    assertThat(appDir.isDirectory(), is(true));
+    assertThat(appDir.exists(), is(true));
+    assertThat(FileUtils.listFiles(appDir, null, true).size(), is(0));
+  }
+
+  @Test
+  public void prepareRubyRailsRootDirectory() throws Exception {
+    ServerFileSystem fileSystem = mock(ServerFileSystem.class);
+    File tempDir = this.temp.getRoot();
+    when(fileSystem.getTempDir()).thenReturn(tempDir);
+
+    File dir = new RailsAppsDeployer(fileSystem, mock(PluginRepository.class)).prepareRailsDirectory();
+
+    assertThat(dir.isDirectory(), is(true));
+    assertThat(dir.exists(), is(true));
+    assertThat(dir.getCanonicalPath(), is(new File(tempDir, "ror").getCanonicalPath()));
+  }
+
+  @Test
+  public void prepareRubyRailsRootDirectory_delete_existing_dir() throws Exception {
+    ServerFileSystem fileSystem = mock(ServerFileSystem.class);
+    File tempDir = this.temp.getRoot();
+    when(fileSystem.getTempDir()).thenReturn(tempDir);
+
+    File file = new File(tempDir, "ror/foo/bar.txt");
+    FileUtils.writeStringToFile(file, "foooo");
+
+    File dir = new RailsAppsDeployer(fileSystem, mock(PluginRepository.class)).prepareRailsDirectory();
+
+    assertThat(dir.isDirectory(), is(true));
+    assertThat(dir.exists(), is(true));
+    assertThat(dir.getCanonicalPath(), is(new File(tempDir, "ror").getCanonicalPath()));
+    assertThat(FileUtils.listFiles(new File(tempDir, "ror"), null, true).size(), is(0));
+  }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/web/RoutesFilterTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/web/RoutesFilterTest.java
new file mode 100644 (file)
index 0000000..92d7dfb
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * 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.server.platform.web;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import javax.servlet.FilterChain;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.server.platform.web.RoutesFilter;
+
+public class RoutesFilterTest {
+
+  HttpServletRequest request = mock(HttpServletRequest.class);
+  HttpServletResponse response = mock(HttpServletResponse.class);
+  FilterChain chain = mock(FilterChain.class);
+
+  RoutesFilter underTest = new RoutesFilter();
+
+  @Before
+  public void setUp() throws Exception {
+    when(request.getContextPath()).thenReturn("/sonarqube");
+  }
+
+  @Test
+  public void send_redirect_when_url_contains_batch_with_jar() throws Exception {
+    when(request.getRequestURI()).thenReturn("/batch/file.jar");
+
+    underTest.doFilter(request, response, chain);
+
+    verify(response).sendRedirect("/sonarqube/batch/file?name=file.jar");
+    verifyZeroInteractions(chain);
+  }
+
+  @Test
+  public void send_redirect_when_url_contains_batch_bootstrap() throws Exception {
+    when(request.getRequestURI()).thenReturn("/batch_bootstrap/index");
+
+    underTest.doFilter(request, response, chain);
+
+    verify(response).sendRedirect("/sonarqube/batch/index");
+    verifyZeroInteractions(chain);
+  }
+
+  @Test
+  public void send_redirect_when_url_contains_api_sources() throws Exception {
+    when(request.getRequestURI()).thenReturn("/api/sources");
+    when(request.getQueryString()).thenReturn("resource=my.project");
+
+    underTest.doFilter(request, response, chain);
+
+    verify(response).sendRedirect("/sonarqube/api/sources/index?resource=my.project");
+    verifyZeroInteractions(chain);
+  }
+
+  @Test
+  public void does_not_redirect_and_execute_remaining_filter_on_unknown_path() throws Exception {
+    when(request.getRequestURI()).thenReturn("/api/issues/search");
+
+    underTest.doFilter(request, response, chain);
+
+    verify(chain).doFilter(request, response);
+    verifyZeroInteractions(response);
+  }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/web/RubyRailsContextListenerTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/web/RubyRailsContextListenerTest.java
new file mode 100644 (file)
index 0000000..8a20e31
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * 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.server.platform.web;
+
+import java.io.IOException;
+import javax.servlet.ServletContextEvent;
+import org.jruby.rack.RackApplicationFactory;
+import org.jruby.rack.servlet.ServletRackContext;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.mockito.Mockito;
+import org.sonar.server.platform.web.PlatformServletContextListener;
+import org.sonar.server.platform.web.RubyRailsContextListener;
+
+import static java.lang.Boolean.TRUE;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class RubyRailsContextListenerTest {
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  ServletContextEvent event = mock(ServletContextEvent.class, Mockito.RETURNS_DEEP_STUBS);
+  RubyRailsContextListener underTest = new RubyRailsContextListener();
+
+  @Test
+  public void do_not_initialize_rails_if_error_during_startup() {
+    when(event.getServletContext().getAttribute(PlatformServletContextListener.STARTED_ATTRIBUTE)).thenReturn(null);
+
+    underTest.contextInitialized(event);
+
+    verify(event.getServletContext(), never()).setAttribute(anyString(), anyObject());
+  }
+
+  @Test
+  public void initialize_rails_if_no_errors_during_startup() {
+    when(event.getServletContext().getAttribute(PlatformServletContextListener.STARTED_ATTRIBUTE)).thenReturn(TRUE);
+    underTest.contextInitialized(event);
+    // Ruby environment is started
+    // See RailsServletContextListener -> RackServletContextListener
+    verify(event.getServletContext()).setAttribute(eq("rack.factory"), anyObject());
+  }
+
+  @Test
+  public void always_propagates_initialization_errors() {
+    expectedException.expect(RuntimeException.class);
+
+    underTest.handleInitializationException(new IOException(), mock(RackApplicationFactory.class), mock(ServletRackContext.class));
+  }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/web/SecurityServletFilterTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/web/SecurityServletFilterTest.java
new file mode 100644 (file)
index 0000000..72d499f
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * 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.server.platform.web;
+
+import java.io.IOException;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.junit.Test;
+import org.sonar.server.platform.web.SecurityServletFilter;
+
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.startsWith;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class SecurityServletFilterTest {
+
+  SecurityServletFilter underTest = new SecurityServletFilter();
+  HttpServletResponse response = mock(HttpServletResponse.class);
+  FilterChain chain = mock(FilterChain.class);
+
+  @Test
+  public void allow_GET_method() throws IOException, ServletException {
+    assertThatMethodIsAllowed("GET");
+  }
+
+  @Test
+  public void allow_HEAD_method() throws IOException, ServletException {
+    assertThatMethodIsAllowed("HEAD");
+  }
+
+  @Test
+  public void allow_PUT_method() throws IOException, ServletException {
+    assertThatMethodIsAllowed("PUT");
+  }
+
+  @Test
+  public void allow_POST_method() throws IOException, ServletException {
+    assertThatMethodIsAllowed("POST");
+  }
+
+  private void assertThatMethodIsAllowed(String httpMethod) throws IOException, ServletException {
+    HttpServletRequest request = newRequest(httpMethod);
+    underTest.doFilter(request, response, chain);
+    verify(response, never()).setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
+    verify(chain).doFilter(request, response);
+  }
+
+  @Test
+  public void deny_OPTIONS_method() throws IOException, ServletException {
+    assertThatMethodIsDenied("OPTIONS");
+  }
+
+  @Test
+  public void deny_TRACE_method() throws IOException, ServletException {
+    assertThatMethodIsDenied("TRACE");
+  }
+
+  private void assertThatMethodIsDenied(String httpMethod) throws IOException, ServletException {
+    underTest.doFilter(newRequest(httpMethod), response, chain);
+    verify(response).setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
+  }
+
+  @Test
+  public void set_secured_headers() throws ServletException, IOException {
+    underTest.init(mock(FilterConfig.class));
+    HttpServletRequest request = newRequest("GET");
+
+    underTest.doFilter(request, response, chain);
+
+    verify(response, times(3)).addHeader(startsWith("X-"), anyString());
+
+    underTest.destroy();
+  }
+
+  private HttpServletRequest newRequest(String httpMethod) {
+    HttpServletRequest req = mock(HttpServletRequest.class);
+    when(req.getMethod()).thenReturn(httpMethod);
+    return req;
+  }
+}
index 82aeee20103b1dc4a089933d6357022f7dff3905..15425dfd2d428f04bde4bbfcccd019e1f1328b9b 100644 (file)
@@ -35,7 +35,7 @@ import org.sonar.api.utils.HttpDownloader;
 import org.sonar.api.utils.SonarException;
 import org.sonar.core.platform.PluginInfo;
 import org.sonar.server.exceptions.BadRequestException;
-import org.sonar.server.platform.DefaultServerFileSystem;
+import org.sonar.server.platform.ServerFileSystem;
 import org.sonar.updatecenter.common.Plugin;
 import org.sonar.updatecenter.common.Release;
 import org.sonar.updatecenter.common.UpdateCenter;
@@ -86,11 +86,11 @@ public class PluginDownloaderTest {
       }
     }).when(httpDownloader).download(any(URI.class), any(File.class));
 
-    DefaultServerFileSystem defaultServerFileSystem = mock(DefaultServerFileSystem.class);
+    ServerFileSystem fs = mock(ServerFileSystem.class);
     downloadDir = testFolder.newFolder("downloads");
-    when(defaultServerFileSystem.getDownloadedPluginsDir()).thenReturn(downloadDir);
+    when(fs.getDownloadedPluginsDir()).thenReturn(downloadDir);
 
-    pluginDownloader = new PluginDownloader(updateCenterMatrixFactory, httpDownloader, defaultServerFileSystem);
+    pluginDownloader = new PluginDownloader(updateCenterMatrixFactory, httpDownloader, fs);
   }
 
   @After
@@ -163,12 +163,12 @@ public class PluginDownloaderTest {
 
   @Test
   public void throw_exception_if_download_dir_is_invalid() throws Exception {
-    DefaultServerFileSystem defaultServerFileSystem = mock(DefaultServerFileSystem.class);
+    ServerFileSystem fs = mock(ServerFileSystem.class);
     // download dir is a file instead of being a directory
     File downloadDir = testFolder.newFile();
-    when(defaultServerFileSystem.getDownloadedPluginsDir()).thenReturn(downloadDir);
+    when(fs.getDownloadedPluginsDir()).thenReturn(downloadDir);
 
-    pluginDownloader = new PluginDownloader(updateCenterMatrixFactory, httpDownloader, defaultServerFileSystem);
+    pluginDownloader = new PluginDownloader(updateCenterMatrixFactory, httpDownloader, fs);
     try {
       pluginDownloader.start();
       fail();
index 0239c1452b50693c9f342df69089d0cdae11c854..ecc861a41176f0396a769621aad10596cdee2a39 100644 (file)
  */
 package org.sonar.server.plugins;
 
+import java.io.File;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
-import org.sonar.core.platform.PluginInfo;
 import org.sonar.core.platform.ExplodedPlugin;
-import org.sonar.server.platform.DefaultServerFileSystem;
-
-import java.io.File;
+import org.sonar.core.platform.PluginInfo;
+import org.sonar.server.platform.ServerFileSystem;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
@@ -37,7 +36,7 @@ public class ServerPluginJarExploderTest {
   @Rule
   public TemporaryFolder temp = new TemporaryFolder();
 
-  DefaultServerFileSystem fs = mock(DefaultServerFileSystem.class);
+  ServerFileSystem fs = mock(ServerFileSystem.class);
   ServerPluginJarExploder underTest = new ServerPluginJarExploder(fs);
 
   @Test
index 5393c4268e71bdeb5d698a07ee40a76e9c8fa8c9..51bc9ebe563cf501adafc2cef5b3d1398320ec88 100644 (file)
@@ -39,7 +39,7 @@ import org.sonar.api.utils.MessageException;
 import org.sonar.api.utils.log.LogTester;
 import org.sonar.core.platform.PluginInfo;
 import org.sonar.core.platform.PluginLoader;
-import org.sonar.server.platform.DefaultServerFileSystem;
+import org.sonar.server.platform.ServerFileSystem;
 import org.sonar.updatecenter.common.Version;
 
 import static org.assertj.core.api.Assertions.assertThat;
@@ -60,7 +60,7 @@ public class ServerPluginRepositoryTest {
 
   Server server = mock(Server.class);
   ServerUpgradeStatus upgradeStatus = mock(ServerUpgradeStatus.class);
-  DefaultServerFileSystem fs = mock(DefaultServerFileSystem.class, Mockito.RETURNS_DEEP_STUBS);
+  ServerFileSystem fs = mock(ServerFileSystem.class, Mockito.RETURNS_DEEP_STUBS);
   PluginLoader pluginLoader = mock(PluginLoader.class);
   ServerPluginRepository underTest = new ServerPluginRepository(server, upgradeStatus, fs, pluginLoader);
 
@@ -384,7 +384,7 @@ public class ServerPluginRepositoryTest {
 
     underTest.getPluginInfos();
   }
-  
+
   private File copyTestPluginTo(String testPluginName, File toDir) throws IOException {
     File jar = TestProjectUtils.jarOf(testPluginName);
     // file is copied because it's supposed to be moved by the test
index 959792795c61ee1252a4cb13a3d0ad412d29b9f0..e21503eea1657de38b47484a1683d65185b0a0a9 100644 (file)
  */
 package org.sonar.server.startup;
 
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
 import org.apache.commons.io.FileUtils;
 import org.hamcrest.core.Is;
 import org.junit.Before;
@@ -27,12 +31,7 @@ import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 import org.sonar.core.platform.PluginInfo;
 import org.sonar.core.platform.PluginRepository;
-import org.sonar.server.platform.DefaultServerFileSystem;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.List;
+import org.sonar.server.platform.ServerFileSystem;
 
 import static org.junit.Assert.assertThat;
 import static org.junit.matchers.JUnitMatchers.containsString;
@@ -44,14 +43,13 @@ public class GeneratePluginIndexTest {
   @Rule
   public TemporaryFolder temp = new TemporaryFolder();
 
-  DefaultServerFileSystem fileSystem;
-  File index;
+  private ServerFileSystem fileSystem = mock(ServerFileSystem.class);
+  private File underTest;
 
   @Before
   public void createIndexFile() {
-    fileSystem = mock(DefaultServerFileSystem.class);
-    index = new File("target/test-tmp/GeneratePluginIndexTest/plugins.txt");
-    when(fileSystem.getPluginIndex()).thenReturn(index);
+    when(fileSystem.getPluginIndex()).thenReturn(underTest);
+    underTest = new File("target/test-tmp/GeneratePluginIndexTest/plugins.txt");
   }
 
   @Test
@@ -63,7 +61,7 @@ public class GeneratePluginIndexTest {
 
     new GeneratePluginIndex(fileSystem, repository).start();
 
-    List<String> lines = FileUtils.readLines(index);
+    List<String> lines = FileUtils.readLines(underTest);
     assertThat(lines.size(), Is.is(2));
     assertThat(lines.get(0), containsString("sqale"));
     assertThat(lines.get(1), containsString("checkstyle"));
index d8d61c1bd22cd5785385be29339b6104936ba41d..86f6bafe0b0a0759d9f8ade157d5fabc83c7c28f 100644 (file)
@@ -21,9 +21,10 @@ package org.sonar.server.startup;
 
 import org.junit.Test;
 import org.sonar.api.web.ServletFilter;
-import org.sonar.server.platform.MasterServletFilter;
+import org.sonar.server.platform.web.MasterServletFilter;
 
 import javax.servlet.ServletException;
+import org.sonar.server.platform.web.RegisterServletFilters;
 
 import static org.mockito.Matchers.anyListOf;
 import static org.mockito.Mockito.mock;
index 78b5140f8ff90ff39b35569702d8b9d48c300e4f..0d25e12ba8da3ff4afe6ff11de2431691da213e3 100644 (file)
  */
 package org.sonar.server.updatecenter.ws;
 
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.sonar.api.server.ws.RailsHandler;
 import org.sonar.api.server.ws.WebService;
-import org.sonar.server.platform.DefaultServerFileSystem;
+import org.sonar.server.platform.ServerFileSystem;
 import org.sonar.server.ws.WsTester;
 
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
 public class UpdateCenterWsTest {
 
   WsTester tester;
@@ -36,7 +36,7 @@ public class UpdateCenterWsTest {
   @Before
   public void setUp() {
     tester = new WsTester(new UpdateCenterWs(
-      new UploadAction(null, mock(DefaultServerFileSystem.class))));
+      new UploadAction(null, mock(ServerFileSystem.class))));
   }
 
   @Test
index 14589593da665e3c90c1c5b485b7cac86396651c..41163d4a98a49ae3bb5a9e1e649f3bc2bf1f1cd0 100644 (file)
 
 package org.sonar.server.updatecenter.ws;
 
-import static java.nio.file.Files.newInputStream;
-import static org.assertj.core.api.Java6Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.core.permission.GlobalPermissions.PROVISIONING;
-import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
-import static org.sonar.test.ExceptionCauseMatcher.hasType;
-
 import java.io.File;
 import java.io.InputStream;
 import java.nio.channels.ClosedChannelException;
@@ -37,11 +29,19 @@ import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.junit.rules.TemporaryFolder;
 import org.sonar.server.exceptions.ForbiddenException;
-import org.sonar.server.platform.DefaultServerFileSystem;
+import org.sonar.server.platform.ServerFileSystem;
 import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.ws.TestResponse;
 import org.sonar.server.ws.WsActionTester;
 
+import static java.nio.file.Files.newInputStream;
+import static org.assertj.core.api.Java6Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.core.permission.GlobalPermissions.PROVISIONING;
+import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
+import static org.sonar.test.ExceptionCauseMatcher.hasType;
+
 public class UploadActionTest {
 
   static final String PLUGIN_NAME = "plugin.jar";
@@ -55,7 +55,7 @@ public class UploadActionTest {
   @Rule
   public UserSessionRule userSession = UserSessionRule.standalone();
 
-  DefaultServerFileSystem fileSystem = mock(DefaultServerFileSystem.class);
+  ServerFileSystem fileSystem = mock(ServerFileSystem.class);
   File pluginDirectory;
 
   File plugin = new File(getClass().getResource("UploadActionTest/plugin.jar").getFile());
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/platform/RailsAppsDeployerTest/FakeRubyRailsApp.jar b/server/sonar-server/src/test/resources/org/sonar/server/platform/RailsAppsDeployerTest/FakeRubyRailsApp.jar
deleted file mode 100644 (file)
index 9ed8666..0000000
Binary files a/server/sonar-server/src/test/resources/org/sonar/server/platform/RailsAppsDeployerTest/FakeRubyRailsApp.jar and /dev/null differ
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/platform/web/RailsAppsDeployerTest/FakeRubyRailsApp.jar b/server/sonar-server/src/test/resources/org/sonar/server/platform/web/RailsAppsDeployerTest/FakeRubyRailsApp.jar
new file mode 100644 (file)
index 0000000..9ed8666
Binary files /dev/null and b/server/sonar-server/src/test/resources/org/sonar/server/platform/web/RailsAppsDeployerTest/FakeRubyRailsApp.jar differ
index bed9e8b5cece964b3c4706933a53873ec11c7281..b86c1a99b9b6b382691612d904dba0a3fca3726d 100644 (file)
@@ -29,7 +29,7 @@
 
   <filter>
     <filter-name>ServletFilters</filter-name>
-    <filter-class>org.sonar.server.platform.MasterServletFilter</filter-class>
+    <filter-class>org.sonar.server.platform.web.MasterServletFilter</filter-class>
   </filter>
   <filter>
     <filter-name>UserSessionFilter</filter-name>
   </filter>
   <filter>
     <filter-name>SecurityFilter</filter-name>
-    <filter-class>org.sonar.server.platform.SecurityServletFilter</filter-class>
+    <filter-class>org.sonar.server.platform.web.SecurityServletFilter</filter-class>
   </filter>
   <filter>
     <filter-name>ProfilingFilter</filter-name>
-    <filter-class>org.sonar.server.platform.ProfilingFilter</filter-class>
+    <filter-class>org.sonar.server.platform.web.ProfilingFilter</filter-class>
     <init-param>
       <param-name>staticDirs</param-name>
       <param-value>/images,/javascripts,/stylesheets</param-value>
@@ -62,7 +62,7 @@
   </filter>
   <filter>
     <filter-name>RoutesFilter</filter-name>
-    <filter-class>org.sonar.server.platform.RoutesFilter</filter-class>
+    <filter-class>org.sonar.server.platform.web.RoutesFilter</filter-class>
   </filter>
 
   <!-- order of execution is important -->
   </session-config>
 
   <listener>
-    <listener-class>org.sonar.server.platform.PlatformServletContextListener</listener-class>
+    <listener-class>org.sonar.server.platform.web.PlatformServletContextListener</listener-class>
   </listener>
   <listener>
-    <listener-class>org.sonar.server.platform.RubyRailsContextListener</listener-class>
+    <listener-class>org.sonar.server.platform.web.RubyRailsContextListener</listener-class>
   </listener>
 
   <mime-mapping>
index 334b663ea88e840c5951645a387066a8be088300..fc84199973d5b00094f087d2eee273854fdb713d 100644 (file)
@@ -30,8 +30,16 @@ import org.sonar.api.server.ServerSide;
 @ComputeEngineSide
 public interface ServerFileSystem {
 
+  /**
+   * Root directory of the server installation
+   * @return an existing directory
+   */
   File getHomeDir();
 
+  /**
+   * Temporary directory, clean up on restarts
+   * @return an existing directory
+   */
   File getTempDir();
 
 }