]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6577 Offline mode in preview mode
authorDuarte Meneses <duarte.meneses@sonarsource.com>
Fri, 17 Jul 2015 08:05:49 +0000 (10:05 +0200)
committerDuarte Meneses <duarte.meneses@sonarsource.com>
Fri, 24 Jul 2015 11:14:57 +0000 (13:14 +0200)
64 files changed:
sonar-batch/src/main/java/org/sonar/batch/bootstrap/AnalysisProperties.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginInstaller.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginPredicate.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/DefaultAnalysisMode.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/bootstrap/ExtensionInstaller.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalMode.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalSettings.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/PersistentCacheProvider.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/WSLoader.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/bootstrap/WSLoaderGlobalProvider.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/cpd/index/IndexFactory.java
sonar-batch/src/main/java/org/sonar/batch/issue/tracking/DefaultServerLineHashesLoader.java
sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java
sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java
sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java
sonar-batch/src/main/java/org/sonar/batch/repository/DefaultGlobalRepositoriesLoader.java
sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoader.java
sonar-batch/src/main/java/org/sonar/batch/repository/DefaultServerIssuesLoader.java
sonar-batch/src/main/java/org/sonar/batch/repository/user/UserRepository.java
sonar-batch/src/main/java/org/sonar/batch/scan/ModuleSettings.java
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectAnalysisMode.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectSettings.java
sonar-batch/src/main/java/org/sonar/batch/scan/WSLoaderProjectProvider.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileBuilder.java
sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactory.java
sonar-batch/src/main/java/org/sonar/batch/scan/report/IssuesReports.java
sonar-batch/src/main/java/org/sonar/batch/util/BatchUtils.java
sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginInstallerTest.java
sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginPredicateTest.java
sonar-batch/src/test/java/org/sonar/batch/bootstrap/DefaultAnalysisModeTest.java [deleted file]
sonar-batch/src/test/java/org/sonar/batch/bootstrap/ExtensionInstallerTest.java
sonar-batch/src/test/java/org/sonar/batch/bootstrap/GlobalModeTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/bootstrap/GlobalSettingsTest.java
sonar-batch/src/test/java/org/sonar/batch/bootstrap/MockHttpServer.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/bootstrap/PersistentCacheProviderTest.java
sonar-batch/src/test/java/org/sonar/batch/bootstrap/ServerClientTest.java
sonar-batch/src/test/java/org/sonar/batch/bootstrap/WSLoaderGlobalProviderTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/bootstrap/WSLoaderTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/bootstrap/WSLoaderTestWithServer.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/cpd/index/IndexFactoryTest.java
sonar-batch/src/test/java/org/sonar/batch/issue/tracking/DefaultServerLineHashesLoaderTest.java
sonar-batch/src/test/java/org/sonar/batch/report/ReportPublisherTest.java
sonar-batch/src/test/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoaderTest.java
sonar-batch/src/test/java/org/sonar/batch/repository/DefaultServerIssuesLoaderTest.java
sonar-batch/src/test/java/org/sonar/batch/repository/user/UserRepositoryTest.java
sonar-batch/src/test/java/org/sonar/batch/scan/DefaultAnalysisModeTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/scan/ModuleSettingsTest.java
sonar-batch/src/test/java/org/sonar/batch/scan/ProjectSettingsTest.java
sonar-batch/src/test/java/org/sonar/batch/scan/WSLoaderProjectProviderTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactoryTest.java
sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderTest.java
sonar-core/src/main/java/org/sonar/core/util/DefaultHttpDownloader.java
sonar-core/src/test/java/org/sonar/core/util/DefaultHttpDownloaderTest.java
sonar-home/src/main/java/org/sonar/home/cache/PersistentCache.java
sonar-home/src/main/java/org/sonar/home/cache/PersistentCacheBuilder.java
sonar-home/src/main/java/org/sonar/home/cache/PersistentCacheLoader.java [new file with mode: 0644]
sonar-home/src/test/java/org/sonar/home/cache/PersistentCacheTest.java
sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/AnalysisMode.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java

index d8b53410f60e2cecd807a13fbf66d1afba29ebe4..8a514c6db45ea82017311f6aa299878c9e6f4601 100644 (file)
@@ -28,7 +28,10 @@ import java.util.Map;
  * coming from sonar-project.properties).
  */
 public class AnalysisProperties extends UserProperties {
-
+  public AnalysisProperties(Map<String, String> properties) {
+    this(properties, null);
+  }
+  
   public AnalysisProperties(Map<String, String> properties, @Nullable String pathToSecretKey) {
     super(properties, pathToSecretKey);
   }
index 1704d30352052adbc1780d1b4b391beb00b845f3..cb6f40dafc13d57f5435587f720c9a8b33165704 100644 (file)
@@ -42,7 +42,7 @@ public class BatchComponents {
     // only static stuff
   }
 
-  public static Collection all(DefaultAnalysisMode analysisMode) {
+  public static Collection all(GlobalMode analysisMode) {
     List components = Lists.newArrayList(
       DefaultResourceTypes.get(),
       // SCM
index 3310365a85612d9c4e88812fbc0cfc0baa81b22b..b6744b5ff4ad87b575ec312107d1ebcdd4798ec2 100644 (file)
  */
 package org.sonar.batch.bootstrap;
 
+import com.google.common.io.Files;
+
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.Lists;
+
 import java.io.File;
 import java.io.IOException;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+
 import org.apache.commons.lang.CharUtils;
 import org.apache.commons.lang.StringUtils;
 import org.sonar.api.Plugin;
@@ -47,12 +51,12 @@ public class BatchPluginInstaller implements PluginInstaller {
   private static final Logger LOG = Loggers.get(BatchPluginInstaller.class);
   private static final String PLUGINS_INDEX_URL = "/deploy/plugins/index.txt";
 
-  private final ServerClient server;
+  private final WSLoader wsLoader;
   private final FileCache fileCache;
   private final BatchPluginPredicate pluginPredicate;
 
-  public BatchPluginInstaller(ServerClient server, FileCache fileCache, BatchPluginPredicate pluginPredicate) {
-    this.server = server;
+  public BatchPluginInstaller(WSLoader wsLoader, FileCache fileCache, BatchPluginPredicate pluginPredicate) {
+    this.wsLoader = wsLoader;
     this.fileCache = fileCache;
     this.pluginPredicate = pluginPredicate;
   }
@@ -95,7 +99,8 @@ public class BatchPluginInstaller implements PluginInstaller {
           } else {
             LOG.info("Download {}", file.getFilename());
           }
-          server.download(url, toFile);
+
+          Files.write(wsLoader.load(url), toFile);
         }
       });
 
@@ -111,7 +116,7 @@ public class BatchPluginInstaller implements PluginInstaller {
   List<RemotePlugin> listRemotePlugins() {
     try {
       Profiler profiler = Profiler.create(LOG).startInfo("Load plugins index");
-      String indexContent = server.request(PLUGINS_INDEX_URL);
+      String indexContent = wsLoader.loadString(PLUGINS_INDEX_URL);
       profiler.stopInfo();
       String[] rows = StringUtils.split(indexContent, CharUtils.LF);
       List<RemotePlugin> result = Lists.newArrayList();
index 435b53d10dbe40a58c9caaa935c5246284324d77..dd55c27758791741eb7045991bc3f7e2ae3b0a83 100644 (file)
@@ -23,17 +23,19 @@ import com.google.common.base.Joiner;
 import com.google.common.base.Predicate;
 import com.google.common.base.Splitter;
 import com.google.common.collect.Lists;
+
 import java.text.MessageFormat;
 import java.util.List;
 import java.util.Set;
+
 import javax.annotation.Nonnull;
+
 import org.apache.commons.lang.StringUtils;
 import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.BatchSide;
 import org.sonar.api.config.Settings;
 import org.sonar.api.utils.log.Logger;
 import org.sonar.api.utils.log.Loggers;
-
 import static com.google.common.collect.Sets.newHashSet;
 
 /**
@@ -50,9 +52,9 @@ public class BatchPluginPredicate implements Predicate<String> {
 
   private final Set<String> whites = newHashSet();
   private final Set<String> blacks = newHashSet();
-  private final DefaultAnalysisMode mode;
+  private final GlobalMode mode;
 
-  public BatchPluginPredicate(Settings settings, DefaultAnalysisMode mode) {
+  public BatchPluginPredicate(Settings settings, GlobalMode mode) {
     this.mode = mode;
     if (mode.isPreview()) {
       // These default values are not supported by Settings because the class CorePlugin
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/DefaultAnalysisMode.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/DefaultAnalysisMode.java
deleted file mode 100644 (file)
index c6b7040..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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.batch.bootstrap;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.batch.AnalysisMode;
-import org.sonar.batch.mediumtest.BatchMediumTester;
-
-import java.text.MessageFormat;
-import java.util.Map;
-
-/**
- * @since 4.0
- */
-public class DefaultAnalysisMode implements AnalysisMode {
-
-  private static final Logger LOG = LoggerFactory.getLogger(DefaultAnalysisMode.class);
-
-  private boolean preview;
-  private boolean incremental;
-  private boolean mediumTestMode;
-
-  public DefaultAnalysisMode(Map<String, String> props) {
-    init(props);
-  }
-
-  @Override
-  public boolean isPreview() {
-    return preview || incremental;
-  }
-
-  @Override
-  public boolean isIncremental() {
-    return incremental;
-  }
-
-  public boolean isMediumTest() {
-    return mediumTestMode;
-  }
-
-  private void init(Map<String, String> props) {
-    if (props.containsKey(CoreProperties.DRY_RUN)) {
-      LOG.warn(MessageFormat.format("Property {0} is deprecated. Please use {1} instead.", CoreProperties.DRY_RUN, CoreProperties.ANALYSIS_MODE));
-      preview = "true".equals(props.get(CoreProperties.DRY_RUN));
-      incremental = false;
-    } else {
-      String mode = props.get(CoreProperties.ANALYSIS_MODE);
-      preview = CoreProperties.ANALYSIS_MODE_PREVIEW.equals(mode);
-      incremental = CoreProperties.ANALYSIS_MODE_INCREMENTAL.equals(mode);
-    }
-    mediumTestMode = "true".equals(props.get(BatchMediumTester.MEDIUM_TEST_ENABLED));
-    if (incremental) {
-      LOG.info("Incremental mode");
-    } else if (preview) {
-      LOG.info("Preview mode");
-    }
-    if (mediumTestMode) {
-      LOG.info("Medium test mode");
-    }
-    // To stay compatible with plugins that use the old property to check mode
-    if (incremental || preview) {
-      props.put(CoreProperties.DRY_RUN, "true");
-    }
-  }
-
-}
index 2899ede562b1cc293cf2a8daa417501d893657e7..aa4f22e4e1e2290bf1fa36e0754c211756b7186a 100644 (file)
@@ -20,7 +20,9 @@
 package org.sonar.batch.bootstrap;
 
 import java.util.List;
+
 import javax.annotation.Nullable;
+
 import org.sonar.api.ExtensionProvider;
 import org.sonar.api.Plugin;
 import org.sonar.batch.bootstrapper.EnvironmentInformation;
@@ -32,18 +34,18 @@ public class ExtensionInstaller {
 
   private final PluginRepository pluginRepository;
   private final EnvironmentInformation env;
-  private final DefaultAnalysisMode analysisMode;
+  private final GlobalMode globalMode;
 
-  public ExtensionInstaller(PluginRepository pluginRepository, EnvironmentInformation env, DefaultAnalysisMode analysisMode) {
+  public ExtensionInstaller(PluginRepository pluginRepository, EnvironmentInformation env, GlobalMode globalMode) {
     this.pluginRepository = pluginRepository;
     this.env = env;
-    this.analysisMode = analysisMode;
+    this.globalMode = globalMode;
   }
 
   public ExtensionInstaller install(ComponentContainer container, ExtensionMatcher matcher) {
 
     // core components
-    for (Object o : BatchComponents.all(analysisMode)) {
+    for (Object o : BatchComponents.all(globalMode)) {
       doInstall(container, matcher, null, o);
     }
 
index dd8660a3795bf0435e6caf1da7689f887adbb473..b61fae769044eb897bd600330bc9d50f74fa5869 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.batch.bootstrap;
 
 import java.util.List;
 import java.util.Map;
+
 import org.sonar.api.CoreProperties;
 import org.sonar.api.Plugin;
 import org.sonar.api.utils.Durations;
@@ -52,7 +53,6 @@ import org.sonar.core.util.DefaultHttpDownloader;
 public class GlobalContainer extends ComponentContainer {
 
   private final Map<String, String> bootstrapProperties;
-  private PersistentCacheProvider persistentCacheProvider;
 
   private GlobalContainer(Map<String, String> bootstrapProperties) {
     super();
@@ -68,14 +68,11 @@ public class GlobalContainer extends ComponentContainer {
   @Override
   protected void doBeforeStart() {
     BootstrapProperties bootstrapProps = new BootstrapProperties(bootstrapProperties);
-    DefaultAnalysisMode analysisMode = new DefaultAnalysisMode(bootstrapProps.properties());
-    add(bootstrapProps, analysisMode);
+    add(bootstrapProps);
     addBootstrapComponents();
   }
 
   private void addBootstrapComponents() {
-    persistentCacheProvider = new PersistentCacheProvider();
-
     add(
       // plugins
       BatchPluginRepository.class,
@@ -86,6 +83,7 @@ public class GlobalContainer extends ComponentContainer {
       ExtensionInstaller.class,
 
       CachesManager.class,
+      GlobalMode.class,
       GlobalSettings.class,
       ServerClient.class,
       Logback.class,
@@ -94,7 +92,8 @@ public class GlobalContainer extends ComponentContainer {
       DefaultHttpDownloader.class,
       UriReader.class,
       new FileCacheProvider(),
-      persistentCacheProvider,
+      new PersistentCacheProvider(),
+      new WSLoaderGlobalProvider(),
       System2.INSTANCE,
       DefaultI18n.class,
       Durations.class,
@@ -129,7 +128,6 @@ public class GlobalContainer extends ComponentContainer {
 
   public void executeAnalysis(Map<String, String> analysisProperties, Object... components) {
     AnalysisProperties props = new AnalysisProperties(analysisProperties, this.getComponentByType(BootstrapProperties.class).property(CoreProperties.ENCRYPTION_SECRET_KEY_PATH));
-    persistentCacheProvider.reconfigure(props);
     new ProjectScanContainer(this, props, components).execute();
   }
 }
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalMode.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalMode.java
new file mode 100644 (file)
index 0000000..83c2249
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.batch.bootstrap;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.sonar.api.CoreProperties;
+
+import java.text.MessageFormat;
+
+public class GlobalMode {
+  private static final Logger LOG = LoggerFactory.getLogger(GlobalMode.class);
+  private boolean preview;
+
+  public boolean isPreview() {
+    return preview;
+  }
+
+  public GlobalMode(BootstrapProperties props) {
+    if (props.property(CoreProperties.DRY_RUN) != null) {
+      LOG.warn(MessageFormat.format("Property {0} is deprecated. Please use {1} instead.", CoreProperties.DRY_RUN, CoreProperties.ANALYSIS_MODE));
+      preview = "true".equals(props.property(CoreProperties.DRY_RUN));
+    } else {
+      String mode = props.property(CoreProperties.ANALYSIS_MODE);
+      preview = CoreProperties.ANALYSIS_MODE_PREVIEW.equals(mode) || CoreProperties.ANALYSIS_MODE_INCREMENTAL.equals(mode) ||
+        CoreProperties.ANALYSIS_MODE_QUICK.equals(mode);
+    }
+
+    if (preview) {
+      LOG.info("Preview global mode");
+    }
+  }
+}
index 718dfd6acb44582e2e570c8d8a45d1f1c85cd41c..db7f0c730fbee575cf60c74eaab1844887b91ef3 100644 (file)
@@ -20,7 +20,9 @@
 package org.sonar.batch.bootstrap;
 
 import com.google.common.collect.ImmutableMap;
+
 import java.util.Map;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.CoreProperties;
@@ -39,17 +41,17 @@ public class GlobalSettings extends Settings {
    * (what will happen, what should the user do, ...) as a value
    */
   private static final Map<String, String> DROPPED_PROPERTIES = ImmutableMap.of(
-      "sonar.jdbc.url", JDBC_SPECIFIC_MESSAGE,
-      "sonar.jdbc.username", JDBC_SPECIFIC_MESSAGE,
-      "sonar.jdbc.password", JDBC_SPECIFIC_MESSAGE
-  );
+    "sonar.jdbc.url", JDBC_SPECIFIC_MESSAGE,
+    "sonar.jdbc.username", JDBC_SPECIFIC_MESSAGE,
+    "sonar.jdbc.password", JDBC_SPECIFIC_MESSAGE
+    );
 
   private final BootstrapProperties bootstrapProps;
   private final GlobalRepositories globalReferentials;
-  private final DefaultAnalysisMode mode;
+  private final GlobalMode mode;
 
   public GlobalSettings(BootstrapProperties bootstrapProps, PropertyDefinitions propertyDefinitions,
-    GlobalRepositories globalReferentials, DefaultAnalysisMode mode) {
+    GlobalRepositories globalReferentials, GlobalMode mode) {
 
     super(propertyDefinitions);
     this.mode = mode;
@@ -63,6 +65,12 @@ public class GlobalSettings extends Settings {
   private void init() {
     addProperties(globalReferentials.globalSettings());
     addProperties(bootstrapProps.properties());
+
+    // To stay compatible with plugins that use the old property to check mode
+    if (mode.isPreview()) {
+      setProperty(CoreProperties.DRY_RUN, "true");
+    }
+
     LOG.info("Server id: " + getString(CoreProperties.SERVER_ID));
   }
 
index be8cce1a11e4f3d08c2605849f05c27abede5190..323b5aa0a0ee2ca15a8cf27311128156bbec05f3 100644 (file)
@@ -20,7 +20,6 @@
 package org.sonar.batch.bootstrap;
 
 import java.nio.file.Paths;
-import java.util.Map;
 import org.picocontainer.injectors.ProviderAdapter;
 import org.sonar.home.cache.PersistentCache;
 import org.sonar.home.cache.PersistentCacheBuilder;
@@ -32,26 +31,14 @@ public class PersistentCacheProvider extends ProviderAdapter {
     if (cache == null) {
       PersistentCacheBuilder builder = new PersistentCacheBuilder(new Slf4jLogger());
 
-      builder.forceUpdate(isForceUpdate(props.properties()));
-
       String home = props.property("sonar.userHome");
       if (home != null) {
         builder.setSonarHome(Paths.get(home));
       }
 
       cache = builder.build();
-    }
+    } 
+    
     return cache;
   }
-
-  public void reconfigure(UserProperties props) {
-    if (cache != null) {
-      cache.reconfigure(isForceUpdate(props.properties()));
-    }
-  }
-
-  private static boolean isForceUpdate(Map<String, String> props) {
-    String enableCache = props.get("sonar.enableHttpCache");
-    return !"true".equals(enableCache);
-  }
 }
index 4f40634966f8e1c2eaeacfd5349876ecb5bac51c..66e2677691f96f63641385ed53766cb600a92595 100644 (file)
  */
 package org.sonar.batch.bootstrap;
 
+import org.sonar.api.utils.HttpDownloader.HttpException;
+
 import com.google.common.base.Joiner;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Strings;
-import com.google.common.io.Files;
-import com.google.common.io.InputSupplier;
 import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.gson.JsonParser;
-import java.io.ByteArrayInputStream;
+
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
 import java.net.URI;
-import java.net.URLEncoder;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.Callable;
+
 import javax.annotation.Nullable;
+
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang.StringEscapeUtils;
 import org.apache.commons.lang.StringUtils;
@@ -47,7 +48,6 @@ import org.sonar.api.batch.BatchSide;
 import org.sonar.api.utils.HttpDownloader;
 import org.sonar.batch.bootstrapper.EnvironmentInformation;
 import org.sonar.core.util.DefaultHttpDownloader;
-import org.sonar.home.cache.PersistentCache;
 
 /**
  * Replace the deprecated org.sonar.batch.ServerMetadata
@@ -60,29 +60,31 @@ public class ServerClient {
 
   private static final String GET = "GET";
   private BootstrapProperties props;
-  private PersistentCache cache;
   private DefaultHttpDownloader.BaseHttpDownloader downloader;
-  private DefaultAnalysisMode mode;
 
-  public ServerClient(BootstrapProperties settings, EnvironmentInformation env, PersistentCache cache, DefaultAnalysisMode mode) {
+  public ServerClient(BootstrapProperties settings, EnvironmentInformation env) {
     this.props = settings;
     this.downloader = new DefaultHttpDownloader.BaseHttpDownloader(settings.properties(), env.toString());
-    this.cache = cache;
-    this.mode = mode;
   }
 
   public String getURL() {
     return StringUtils.removeEnd(StringUtils.defaultIfBlank(props.property("sonar.host.url"), "http://localhost:9000"), "/");
   }
 
+  public URI getURI(String pathStartingWithSlash) {
+    Preconditions.checkArgument(pathStartingWithSlash.startsWith("/"), "Path must start with slash /");
+    String path = StringEscapeUtils.escapeHtml(pathStartingWithSlash);
+    return URI.create(getURL() + path);
+  }
+
   public void download(String pathStartingWithSlash, File toFile) {
-    download(pathStartingWithSlash, toFile, null);
+    download(pathStartingWithSlash, toFile, null, null);
   }
 
-  public void download(String pathStartingWithSlash, File toFile, @Nullable Integer readTimeoutMillis) {
+  public void download(String pathStartingWithSlash, File toFile, @Nullable Integer connectTimeoutMillis, @Nullable Integer readTimeoutMillis) {
     try {
-      InputSupplier<InputStream> inputSupplier = doRequest(pathStartingWithSlash, GET, readTimeoutMillis);
-      Files.copy(inputSupplier, toFile);
+      InputStream is = load(pathStartingWithSlash, GET, false, connectTimeoutMillis, readTimeoutMillis);
+      Files.copy(is, toFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
     } catch (HttpDownloader.HttpException he) {
       throw handleHttpException(he);
     } catch (IOException e) {
@@ -90,80 +92,41 @@ public class ServerClient {
     }
   }
 
-  public String request(String pathStartingWithSlash) {
-    return request(pathStartingWithSlash, GET, true);
-  }
-
-  public String request(String pathStartingWithSlash, String requestMethod) {
-    return request(pathStartingWithSlash, requestMethod, true);
+  public String downloadString(String pathStartingWithSlash) {
+    return downloadString(pathStartingWithSlash, GET, true, null);
   }
 
-  public String request(String pathStartingWithSlash, boolean wrapHttpException) {
-    return request(pathStartingWithSlash, GET, wrapHttpException, null);
-  }
-
-  public String request(String pathStartingWithSlash, String requestMethod, boolean wrapHttpException) {
-    return request(pathStartingWithSlash, requestMethod, wrapHttpException, null);
-  }
-
-  public String request(String pathStartingWithSlash, String requestMethod, boolean wrapHttpException, @Nullable Integer timeoutMillis) {
-    final byte[] buf = load(pathStartingWithSlash, requestMethod, wrapHttpException, timeoutMillis);
+  public String downloadString(String pathStartingWithSlash, String requestMethod, boolean wrapHttpException, @Nullable Integer timeoutMillis) {
+    InputStream is = load(pathStartingWithSlash, requestMethod, wrapHttpException, null, timeoutMillis);
     try {
-      return new String(buf, "UTF-8");
-    } catch (UnsupportedEncodingException e) {
+      return new String(IOUtils.toByteArray(is), "UTF-8");
+    } catch (IOException e) {
       throw new IllegalStateException(String.format("Unable to request: %s", pathStartingWithSlash), e);
     }
   }
 
-  public InputSupplier<InputStream> doRequest(String pathStartingWithSlash, String requestMethod, @Nullable Integer timeoutMillis) {
-    final byte[] buf = load(pathStartingWithSlash, requestMethod, false, timeoutMillis);
-
-    return new InputSupplier<InputStream>() {
-      @Override
-      public InputStream getInput() throws IOException {
-        return new ByteArrayInputStream(buf);
-      }
-    };
-  }
-
-  private byte[] load(String pathStartingWithSlash, String requestMethod, boolean wrapHttpException, @Nullable Integer timeoutMillis) {
-    Preconditions.checkArgument(pathStartingWithSlash.startsWith("/"), "Path must start with slash /");
-    String path = StringEscapeUtils.escapeHtml(pathStartingWithSlash);
-    URI uri = URI.create(getURL() + path);
+  /**
+   * @throws IllegalStateException on I/O error, not limited to the network connection and if HTTP response code > 400 and wrapHttpException is true
+   * @throws HttpException if HTTP response code > 400 and wrapHttpException is false
+   */
+  public InputStream load(String pathStartingWithSlash, String requestMethod, boolean wrapHttpException, @Nullable Integer connectTimeoutMs,
+    @Nullable Integer readTimeoutMs) {
+    URI uri = getURI(pathStartingWithSlash);
 
     try {
-      if (GET.equals(requestMethod) && mode.isPreview()) {
-        return cache.get(uri.toString(), new HttpValueLoader(uri, requestMethod, timeoutMillis));
+      if (Strings.isNullOrEmpty(getLogin())) {
+        return downloader.newInputSupplier(uri, requestMethod, connectTimeoutMs, readTimeoutMs).getInput();
       } else {
-        return new HttpValueLoader(uri, requestMethod, timeoutMillis).call();
+        return downloader.newInputSupplier(uri, requestMethod, getLogin(), getPassword(), connectTimeoutMs, readTimeoutMs).getInput();
       }
     } catch (HttpDownloader.HttpException e) {
-      throw wrapHttpException ? handleHttpException(e) : e;
-    } catch (Exception e) {
-      throw new IllegalStateException(String.format("Unable to request: %s", uri), e);
-    }
-  }
-
-  private class HttpValueLoader implements Callable<byte[]> {
-    private URI uri;
-    private String requestMethod;
-    private Integer timeoutMillis;
-
-    public HttpValueLoader(URI uri, String requestMethod, Integer timeoutMillis) {
-      this.uri = uri;
-      this.requestMethod = requestMethod;
-      this.timeoutMillis = timeoutMillis;
-    }
-
-    @Override
-    public byte[] call() throws Exception {
-      InputSupplier<InputStream> inputSupplier;
-      if (Strings.isNullOrEmpty(getLogin())) {
-        inputSupplier = downloader.newInputSupplier(uri, requestMethod, timeoutMillis);
+      if (wrapHttpException) {
+        throw handleHttpException(e);
       } else {
-        inputSupplier = downloader.newInputSupplier(uri, requestMethod, getLogin(), getPassword(), timeoutMillis);
+        throw e;
       }
-      return IOUtils.toByteArray(inputSupplier.getInput());
+    } catch (IOException e) {
+      throw new IllegalStateException(String.format("Unable to request: %s", uri), e);
     }
   }
 
@@ -207,14 +170,4 @@ public class ServerClient {
   public String getPassword() {
     return props.property(CoreProperties.PASSWORD);
   }
-
-  public static String encodeForUrl(String url) {
-    try {
-      return URLEncoder.encode(url, "UTF-8");
-
-    } catch (UnsupportedEncodingException e) {
-      throw new IllegalStateException("Encoding not supported", e);
-    }
-  }
-
 }
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/WSLoader.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/WSLoader.java
new file mode 100644 (file)
index 0000000..d475e68
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.batch.bootstrap;
+
+import javax.annotation.Nonnull;
+
+import org.sonar.api.utils.HttpDownloader;
+import com.google.common.io.ByteSource;
+import org.apache.commons.io.IOUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+import static org.sonar.batch.bootstrap.WSLoader.ServerStatus.*;
+import static org.sonar.batch.bootstrap.WSLoader.LoadStrategy.*;
+import org.sonar.home.cache.PersistentCache;
+
+public class WSLoader {
+  private static final String FAIL_MSG = "Server is not accessible and data is not cached";
+  private static final int CONNECT_TIMEOUT = 5000;
+  private static final int READ_TIMEOUT = 10000;
+  private static final String REQUEST_METHOD = "GET";
+
+  public enum ServerStatus {
+    UNKNOWN, ACCESSIBLE, NOT_ACCESSIBLE;
+  }
+
+  public enum LoadStrategy {
+    SERVER_FIRST, CACHE_FIRST;
+  }
+
+  private LoadStrategy loadStrategy;
+  private boolean cacheEnabled;
+  private ServerStatus serverStatus;
+  private ServerClient client;
+  private PersistentCache cache;
+
+  public WSLoader(boolean cacheEnabled, PersistentCache cache, ServerClient client) {
+    this.cacheEnabled = cacheEnabled;
+    this.loadStrategy = CACHE_FIRST;
+    this.serverStatus = UNKNOWN;
+    this.cache = cache;
+    this.client = client;
+  }
+
+  public WSLoader(PersistentCache cache, ServerClient client) {
+    this(false, cache, client);
+  }
+
+  public ByteSource loadSource(String id) {
+    return ByteSource.wrap(load(id));
+  }
+
+  public String loadString(String id) {
+    return new String(load(id), StandardCharsets.UTF_8);
+  }
+
+  @Nonnull
+  public byte[] load(String id) {
+    if (loadStrategy == CACHE_FIRST) {
+      return loadFromCacheFirst(id);
+    } else {
+      return loadFromServerFirst(id);
+    }
+  }
+
+  public void setStrategy(LoadStrategy strategy) {
+    this.loadStrategy = strategy;
+  }
+
+  public LoadStrategy getStrategy() {
+    return this.loadStrategy;
+  }
+
+  public void setCacheEnabled(boolean enabled) {
+    this.cacheEnabled = enabled;
+  }
+
+  public boolean isCacheEnabled() {
+    return this.cacheEnabled;
+  }
+
+  private void switchToOffline() {
+    serverStatus = NOT_ACCESSIBLE;
+  }
+
+  private void switchToOnline() {
+    serverStatus = ACCESSIBLE;
+  }
+
+  private boolean isOffline() {
+    return serverStatus == NOT_ACCESSIBLE;
+  }
+
+  @Nonnull
+  private byte[] loadFromCacheFirst(String id) {
+    byte[] cached = loadFromCache(id);
+    if (cached != null) {
+      return cached;
+    }
+
+    try {
+      return loadFromServer(id);
+    } catch (Exception e) {
+      if (e.getCause() instanceof HttpDownloader.HttpException) {
+        throw e;
+      }
+    }
+
+    throw new IllegalStateException(FAIL_MSG);
+  }
+
+  @Nonnull
+  private byte[] loadFromServerFirst(String id) {
+    try {
+      return loadFromServer(id);
+    } catch (Exception serverException) {
+      if (serverException.getCause() instanceof HttpDownloader.HttpException) {
+        // http exceptions should always be thrown (no fallback)
+        throw serverException;
+      }
+      byte[] cached = loadFromCache(id);
+      if (cached != null) {
+        return cached;
+      }
+    }
+    
+    throw new IllegalStateException(FAIL_MSG);
+  }
+
+  private byte[] loadFromCache(String id) {
+    if (!cacheEnabled) {
+      return null;
+    }
+
+    try {
+      return cache.get(client.getURI(id).toString(), null);
+    } catch (IOException e) {
+      throw new IllegalStateException(e);
+    }
+  }
+
+  private byte[] loadFromServer(String id) {
+    if (isOffline()) {
+      throw new IllegalStateException("Server is not accessible");
+    }
+
+    try {
+      InputStream is = client.load(id, REQUEST_METHOD, true, CONNECT_TIMEOUT, READ_TIMEOUT);
+      switchToOnline();
+      byte[] value = IOUtils.toByteArray(is);
+      if (cacheEnabled) {
+        cache.put(client.getURI(id).toString(), value);
+      }
+      return value;
+    } catch (IllegalStateException e) {
+      switchToOffline();
+      throw e;
+    } catch (Exception e) {
+      switchToOffline();
+      throw new IllegalStateException(e);
+    }
+  }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/WSLoaderGlobalProvider.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/WSLoaderGlobalProvider.java
new file mode 100644 (file)
index 0000000..2d9a762
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.batch.bootstrap;
+
+import org.picocontainer.injectors.ProviderAdapter;
+
+import org.sonar.batch.bootstrap.WSLoader.LoadStrategy;
+
+import java.util.Map;
+
+import org.sonar.home.cache.PersistentCache;
+
+public class WSLoaderGlobalProvider extends ProviderAdapter {
+  private static final LoadStrategy DEFAULT_STRATEGY = LoadStrategy.SERVER_FIRST;
+  private WSLoader wsLoader;
+
+  public WSLoader provide(BootstrapProperties props, GlobalMode mode, PersistentCache cache, ServerClient client) {
+    if (wsLoader == null) {
+      wsLoader = new WSLoader(isCacheEnabled(props.properties(), mode.isPreview()), cache, client);
+      wsLoader.setStrategy(DEFAULT_STRATEGY);
+    }
+    return wsLoader;
+  }
+
+  private static boolean isCacheEnabled(Map<String, String> props, boolean isPreview) {
+    String enableOffline = props.get("sonar.enableOffline");
+    return isPreview && "true".equals(enableOffline);
+  }
+}
index 26fa317b8bbc269e8cea7ef7e1e957055ec9a759..d9d6a444080851233b9cbb55a0fbef3f77c5901f 100644 (file)
  */
 package org.sonar.batch.cpd.index;
 
+import org.sonar.batch.scan.ProjectAnalysisMode;
+
 import com.google.common.annotations.VisibleForTesting;
+
 import javax.annotation.Nullable;
+
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.BatchSide;
 import org.sonar.api.config.Settings;
 import org.sonar.api.resources.Project;
-import org.sonar.batch.bootstrap.DefaultAnalysisMode;
 
 @BatchSide
 public class IndexFactory {
 
   private final Settings settings;
-  private final DefaultAnalysisMode mode;
+  private final ProjectAnalysisMode mode;
 
-  public IndexFactory(DefaultAnalysisMode mode, Settings settings) {
+  public IndexFactory(ProjectAnalysisMode mode, Settings settings) {
     this.mode = mode;
     this.settings = settings;
   }
index f7fced2829d4bf31b88865f747382e01f12fce63..121783e1da54fea3509b5cffe06ce72e9de10658 100644 (file)
  */
 package org.sonar.batch.issue.tracking;
 
+import org.sonar.batch.util.BatchUtils;
+
+import org.sonar.batch.bootstrap.WSLoader;
 import com.google.common.base.Splitter;
 import com.google.common.collect.Iterators;
 import org.sonar.api.utils.log.Loggers;
 import org.sonar.api.utils.log.Profiler;
-import org.sonar.batch.bootstrap.ServerClient;
 
 public class DefaultServerLineHashesLoader implements ServerLineHashesLoader {
 
-  private final ServerClient server;
+  private final WSLoader wsLoader;
 
-  public DefaultServerLineHashesLoader(ServerClient server) {
-    this.server = server;
+  public DefaultServerLineHashesLoader(WSLoader wsLoader) {
+    this.wsLoader = wsLoader;
   }
 
   @Override
@@ -44,7 +46,7 @@ public class DefaultServerLineHashesLoader implements ServerLineHashesLoader {
       .addContext("file", fileKey)
       .startDebug("Load line hashes");
     try {
-      return server.request("/api/sources/hash?key=" + ServerClient.encodeForUrl(fileKey));
+      return wsLoader.loadString("/api/sources/hash?key=" + BatchUtils.encodeForUrl(fileKey));
     } finally {
       profiler.stopDebug();
     }
index c553aeb349a1ee1a0e3c06af0b4df0d8a5ac16c7..370c3f32ca92094a4e634f9bd69f0f3d16ce6d6b 100644 (file)
@@ -190,7 +190,7 @@ public class BatchMediumTester {
     }
     TaskBuilder builder = new TaskBuilder(this);
     builder.property("sonar.projectBaseDir", sonarProps.getParentFile().getAbsolutePath());
-    for (Map.Entry entry : prop.entrySet()) {
+    for (Map.Entry<Object, Object> entry : prop.entrySet()) {
       builder.property(entry.getKey().toString(), entry.getValue().toString());
     }
     return builder;
@@ -238,7 +238,7 @@ public class BatchMediumTester {
       return ref.globalSettings();
     }
 
-    public FakeGlobalRepositoriesLoader add(Metric metric) {
+    public FakeGlobalRepositoriesLoader add(Metric<?> metric) {
       Boolean optimizedBestValue = metric.isOptimizedBestValue();
       ref.metrics().add(new org.sonar.batch.protocol.input.Metric(metricId,
         metric.key(),
index 6acf8f31cb25d5c48992c088527a807645a89b2a..b49448be10cc57a1219e7563e84e583e897cca55 100644 (file)
  */
 package org.sonar.batch.phases;
 
+import org.sonar.batch.scan.ProjectAnalysisMode;
+
 import org.sonar.api.batch.SensorContext;
 import org.sonar.api.resources.Project;
-import org.sonar.batch.bootstrap.DefaultAnalysisMode;
 import org.sonar.batch.events.BatchStepEvent;
 import org.sonar.batch.events.EventBus;
 import org.sonar.batch.index.DefaultIndex;
@@ -48,7 +49,7 @@ public final class PhaseExecutor {
   private final QProfileVerifier profileVerifier;
   private final IssueExclusionsLoader issueExclusionsLoader;
   private final IssuesReports issuesReport;
-  private final DefaultAnalysisMode analysisMode;
+  private final ProjectAnalysisMode analysisMode;
   private final LocalIssueTracking localIssueTracking;
 
   public PhaseExecutor(InitializersExecutor initializersExecutor, PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor,
index fb6075057dd6fca65d3cf1a284a7260b047dcb6a..2435330b6135d38fae4fb02a1384e6c210ba41b7 100644 (file)
  */
 package org.sonar.batch.report;
 
+import org.sonar.batch.scan.ProjectAnalysisMode;
+
+import org.sonar.batch.util.BatchUtils;
 import com.github.kevinsawicki.http.HttpRequest;
 import com.google.common.annotations.VisibleForTesting;
+
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -28,6 +32,7 @@ import java.net.MalformedURLException;
 import java.net.URISyntaxException;
 import java.net.URL;
 import java.util.Date;
+
 import org.apache.commons.io.FileUtils;
 import org.picocontainer.Startable;
 import org.slf4j.Logger;
@@ -39,11 +44,9 @@ import org.sonar.api.config.Settings;
 import org.sonar.api.platform.Server;
 import org.sonar.api.utils.TempFolder;
 import org.sonar.api.utils.ZipUtils;
-import org.sonar.batch.bootstrap.DefaultAnalysisMode;
 import org.sonar.batch.bootstrap.ServerClient;
 import org.sonar.batch.protocol.output.BatchReportWriter;
 import org.sonar.batch.scan.ImmutableProjectReactor;
-
 import static java.lang.String.format;
 
 @BatchSide
@@ -57,7 +60,7 @@ public class ReportPublisher implements Startable {
   private final Server server;
   private final Settings settings;
   private final ImmutableProjectReactor projectReactor;
-  private final DefaultAnalysisMode analysisMode;
+  private final ProjectAnalysisMode analysisMode;
   private final TempFolder temp;
 
   private ReportPublisherStep[] publishers;
@@ -66,7 +69,7 @@ public class ReportPublisher implements Startable {
   private BatchReportWriter writer;
 
   public ReportPublisher(Settings settings, ServerClient serverClient, Server server,
-    ImmutableProjectReactor projectReactor, DefaultAnalysisMode analysisMode, TempFolder temp, ReportPublisherStep[] publishers) {
+    ImmutableProjectReactor projectReactor, ProjectAnalysisMode analysisMode, TempFolder temp, ReportPublisherStep[] publishers) {
     this.serverClient = serverClient;
     this.server = server;
     this.projectReactor = projectReactor;
@@ -134,7 +137,7 @@ public class ReportPublisher implements Startable {
   void sendOrDumpReport(File report) {
     ProjectDefinition projectDefinition = projectReactor.getRoot();
     String effectiveKey = projectDefinition.getKeyWithBranch();
-    String relativeUrl = "/api/computation/submit_report?projectKey=" + effectiveKey + "&projectName=" + ServerClient.encodeForUrl(projectDefinition.getName());
+    String relativeUrl = "/api/computation/submit_report?projectKey=" + effectiveKey + "&projectName=" + BatchUtils.encodeForUrl(projectDefinition.getName());
 
     String dumpDirLocation = settings.getString(DUMP_REPORT_PROP_KEY);
     if (dumpDirLocation == null) {
index 428e9bccab58d40a10e1265e91851826ff4d92c4..beb47749d6f62b0e68c3d792dff85cfb19e73860 100644 (file)
  */
 package org.sonar.batch.repository;
 
-import org.sonar.batch.bootstrap.ServerClient;
+import org.sonar.batch.bootstrap.WSLoader;
+
 import org.sonar.batch.protocol.input.GlobalRepositories;
 
 public class DefaultGlobalRepositoriesLoader implements GlobalRepositoriesLoader {
 
   private static final String BATCH_GLOBAL_URL = "/batch/global";
 
-  private final ServerClient serverClient;
+  private final WSLoader wsLoader;
 
-  public DefaultGlobalRepositoriesLoader(ServerClient serverClient) {
-    this.serverClient = serverClient;
+  public DefaultGlobalRepositoriesLoader(WSLoader wsLoader) {
+    this.wsLoader = wsLoader;
   }
 
   @Override
   public GlobalRepositories load() {
-    return GlobalRepositories.fromJson(serverClient.request(BATCH_GLOBAL_URL));
+    return GlobalRepositories.fromJson(wsLoader.loadString(BATCH_GLOBAL_URL));
   }
 
 }
index 71f4953769e572a30d4f6f49867b0bdb25227a66..da00896766291e242ecf009845f0ac2a79b4e86a 100644 (file)
  */
 package org.sonar.batch.repository;
 
+import org.sonar.batch.scan.ProjectAnalysisMode;
+
+import org.sonar.batch.util.BatchUtils;
+import org.sonar.batch.bootstrap.WSLoader;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.batch.bootstrap.ProjectReactor;
-import org.sonar.batch.bootstrap.DefaultAnalysisMode;
-import org.sonar.batch.bootstrap.ServerClient;
 import org.sonar.batch.bootstrap.AnalysisProperties;
 import org.sonar.batch.protocol.input.ProjectRepositories;
 import org.sonar.batch.rule.ModuleQProfiles;
@@ -34,25 +36,25 @@ public class DefaultProjectRepositoriesLoader implements ProjectRepositoriesLoad
 
   private static final String BATCH_PROJECT_URL = "/batch/project";
 
-  private final ServerClient serverClient;
-  private final DefaultAnalysisMode analysisMode;
+  private final WSLoader wsLoader;
+  private final ProjectAnalysisMode analysisMode;
 
-  public DefaultProjectRepositoriesLoader(ServerClient serverClient, DefaultAnalysisMode analysisMode) {
-    this.serverClient = serverClient;
+  public DefaultProjectRepositoriesLoader(WSLoader wsLoader, ProjectAnalysisMode analysisMode) {
+    this.wsLoader = wsLoader;
     this.analysisMode = analysisMode;
   }
 
   @Override
   public ProjectRepositories load(ProjectReactor reactor, AnalysisProperties taskProperties) {
     String projectKey = reactor.getRoot().getKeyWithBranch();
-    String url = BATCH_PROJECT_URL + "?key=" + ServerClient.encodeForUrl(projectKey);
+    String url = BATCH_PROJECT_URL + "?key=" + BatchUtils.encodeForUrl(projectKey);
     if (taskProperties.properties().containsKey(ModuleQProfiles.SONAR_PROFILE_PROP)) {
       LOG.warn("Ability to set quality profile from command line using '" + ModuleQProfiles.SONAR_PROFILE_PROP
         + "' is deprecated and will be dropped in a future SonarQube version. Please configure quality profile used by your project on SonarQube server.");
-      url += "&profile=" + ServerClient.encodeForUrl(taskProperties.properties().get(ModuleQProfiles.SONAR_PROFILE_PROP));
+      url += "&profile=" + BatchUtils.encodeForUrl(taskProperties.properties().get(ModuleQProfiles.SONAR_PROFILE_PROP));
     }
     url += "&preview=" + analysisMode.isPreview();
-    return ProjectRepositories.fromJson(serverClient.request(url));
+    return ProjectRepositories.fromJson(wsLoader.loadString(url));
   }
 
 }
index e71e9eb54cbd6ece9b6b0cef2914824f19e47dd2..b6d9723bb55c067a413d28319bd7654535bf3e1f 100644 (file)
  */
 package org.sonar.batch.repository;
 
+import org.sonar.batch.util.BatchUtils;
+
+import com.google.common.io.ByteSource;
+import org.sonar.batch.bootstrap.WSLoader;
 import com.google.common.base.Function;
-import com.google.common.io.InputSupplier;
-import org.sonar.batch.bootstrap.ServerClient;
 import org.sonar.batch.protocol.input.BatchInput.ServerIssue;
-import org.sonar.api.utils.HttpDownloader;
 
 import java.io.IOException;
 import java.io.InputStream;
 
 public class DefaultServerIssuesLoader implements ServerIssuesLoader {
 
-  private final ServerClient serverClient;
+  private final WSLoader wsLoader;
 
-  public DefaultServerIssuesLoader(ServerClient serverClient) {
-    this.serverClient = serverClient;
+  public DefaultServerIssuesLoader(WSLoader wsLoader) {
+    this.wsLoader = wsLoader;
   }
 
   @Override
   public void load(String componentKey, Function<ServerIssue, Void> consumer, boolean incremental) {
-    try {
-      InputSupplier<InputStream> request = serverClient.doRequest("/batch/issues?key=" + ServerClient.encodeForUrl(componentKey), "GET", null);
-      parseIssues(request, consumer);
-    } catch (HttpDownloader.HttpException e) {
-      throw serverClient.handleHttpException(e);
-    }
+    ByteSource request = wsLoader.loadSource("/batch/issues?key=" + BatchUtils.encodeForUrl(componentKey));
+    parseIssues(request, consumer);
   }
 
-  private static void parseIssues(InputSupplier<InputStream> input, Function<ServerIssue, Void> consumer) {
-    try (InputStream is = input.getInput()) {
+  private static void parseIssues(ByteSource input, Function<ServerIssue, Void> consumer) {
+    try (InputStream is = input.openStream()) {
       ServerIssue previousIssue = ServerIssue.parseDelimitedFrom(is);
       while (previousIssue != null) {
         consumer.apply(previousIssue);
index 7208086c308fc5cfa09825307292e89b870ed6ae..c6f1d8ae58ca908a305b28adb5ee61bd80ddc829 100644 (file)
  */
 package org.sonar.batch.repository.user;
 
+import org.sonar.batch.util.BatchUtils;
+
+import org.sonar.batch.bootstrap.WSLoader;
+import com.google.common.io.ByteSource;
 import com.google.common.base.Function;
 import com.google.common.base.Joiner;
 import com.google.common.collect.Lists;
-import com.google.common.io.InputSupplier;
-import org.sonar.api.utils.HttpDownloader;
-import org.sonar.batch.bootstrap.ServerClient;
 import org.sonar.batch.protocol.input.BatchInput;
 
 import java.io.IOException;
@@ -36,10 +37,10 @@ import java.util.List;
 
 public class UserRepository {
 
-  private ServerClient serverClient;
+  private WSLoader wsLoader;
 
-  public UserRepository(ServerClient serverClient) {
-    this.serverClient = serverClient;
+  public UserRepository(WSLoader wsLoader) {
+    this.wsLoader = wsLoader;
   }
 
   public Collection<BatchInput.User> loadFromWs(List<String> userLogins) {
@@ -47,25 +48,20 @@ public class UserRepository {
       return Collections.emptyList();
     }
 
-    try {
-      InputSupplier<InputStream> request = serverClient.doRequest("/batch/users?logins=" + Joiner.on(',').join(Lists.transform(userLogins, new Function<String, String>() {
-        @Override
-        public String apply(String input) {
-          return ServerClient.encodeForUrl(input);
-        }
-      })), "GET", null);
-
-      return parseUsers(request);
+    ByteSource source = wsLoader.loadSource("/batch/users?logins=" + Joiner.on(',').join(Lists.transform(userLogins, new Function<String, String>() {
+      @Override
+      public String apply(String input) {
+        return BatchUtils.encodeForUrl(input);
+      }
+    })));
 
-    } catch (HttpDownloader.HttpException e) {
-      throw serverClient.handleHttpException(e);
-    }
+    return parseUsers(source);
   }
 
-  private static Collection<BatchInput.User> parseUsers(InputSupplier<InputStream> input) {
+  private static Collection<BatchInput.User> parseUsers(ByteSource input) {
     List<BatchInput.User> users = new ArrayList<>();
 
-    try (InputStream is = input.getInput()) {
+    try (InputStream is = input.openStream()) {
       BatchInput.User user = BatchInput.User.parseDelimitedFrom(is);
       while (user != null) {
         users.add(user);
index bf1d0815edea3973a946a424e08b2bb34c6acf23..cf5a3c44fd9f151ae0651784c8ec74d0e72d6384 100644 (file)
 package org.sonar.batch.scan;
 
 import com.google.common.collect.Lists;
+
 import java.util.List;
+
 import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.api.config.Settings;
 import org.sonar.api.utils.MessageException;
-import org.sonar.batch.bootstrap.DefaultAnalysisMode;
 import org.sonar.batch.bootstrap.GlobalSettings;
 import org.sonar.batch.protocol.input.ProjectRepositories;
 
@@ -35,10 +36,10 @@ import org.sonar.batch.protocol.input.ProjectRepositories;
 public class ModuleSettings extends Settings {
 
   private final ProjectRepositories projectReferentials;
-  private DefaultAnalysisMode analysisMode;
+  private ProjectAnalysisMode analysisMode;
 
   public ModuleSettings(GlobalSettings batchSettings, ProjectDefinition moduleDefinition, ProjectRepositories projectReferentials,
-    DefaultAnalysisMode analysisMode) {
+    ProjectAnalysisMode analysisMode) {
     super(batchSettings.getDefinitions());
     this.projectReferentials = projectReferentials;
     this.analysisMode = analysisMode;
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectAnalysisMode.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectAnalysisMode.java
new file mode 100644 (file)
index 0000000..1f54486
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.batch.scan;
+
+import org.sonar.batch.bootstrap.AnalysisProperties;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.AnalysisMode;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+
+import java.text.MessageFormat;
+import java.util.Map;
+
+/**
+ * @since 4.0
+ */
+public class ProjectAnalysisMode implements AnalysisMode {
+
+  private static final Logger LOG = LoggerFactory.getLogger(ProjectAnalysisMode.class);
+
+  private boolean preview;
+  private boolean incremental;
+  private boolean quick;
+  private boolean mediumTestMode;
+
+  public ProjectAnalysisMode(AnalysisProperties props) {
+    init(props.properties());
+  }
+
+  @Override
+  public boolean isPreview() {
+    return preview || incremental || quick;
+  }
+
+  @Override
+  public boolean isQuick() {
+    return quick;
+  }
+
+  @Override
+  public boolean isIncremental() {
+    return incremental;
+  }
+
+  public boolean isMediumTest() {
+    return mediumTestMode;
+  }
+
+  private void init(Map<String, String> props) {
+    if (props.containsKey(CoreProperties.DRY_RUN)) {
+      LOG.warn(MessageFormat.format("Property {0} is deprecated. Please use {1} instead.", CoreProperties.DRY_RUN, CoreProperties.ANALYSIS_MODE));
+      preview = "true".equals(props.get(CoreProperties.DRY_RUN));
+      incremental = false;
+    } else {
+      String mode = props.get(CoreProperties.ANALYSIS_MODE);
+      preview = CoreProperties.ANALYSIS_MODE_PREVIEW.equals(mode);
+      incremental = CoreProperties.ANALYSIS_MODE_INCREMENTAL.equals(mode);
+      quick = CoreProperties.ANALYSIS_MODE_QUICK.equals(mode);
+    }
+    mediumTestMode = "true".equals(props.get(BatchMediumTester.MEDIUM_TEST_ENABLED));
+    if (incremental) {
+      LOG.info("Incremental mode");
+    } else if (preview) {
+      LOG.info("Preview mode");
+    } else if (quick) {
+      LOG.info("Quick mode");
+    }
+    if (mediumTestMode) {
+      LOG.info("Medium test mode");
+    }
+  }
+
+}
index 06d0a4423a205f6bccc15d43121c98ad30033e73..1e0b5d4571bb449917d14c2802f5f5d2f33b1613 100644 (file)
@@ -34,7 +34,6 @@ import org.sonar.batch.DefaultFileLinesContextFactory;
 import org.sonar.batch.DefaultProjectTree;
 import org.sonar.batch.ProjectConfigurator;
 import org.sonar.batch.bootstrap.AnalysisProperties;
-import org.sonar.batch.bootstrap.DefaultAnalysisMode;
 import org.sonar.batch.bootstrap.ExtensionInstaller;
 import org.sonar.batch.bootstrap.ExtensionMatcher;
 import org.sonar.batch.bootstrap.ExtensionUtils;
@@ -83,7 +82,6 @@ public class ProjectScanContainer extends ComponentContainer {
 
   private static final Logger LOG = Loggers.get(ProjectScanContainer.class);
 
-  private final DefaultAnalysisMode analysisMode;
   private final Object[] components;
   private final AnalysisProperties props;
 
@@ -91,7 +89,6 @@ public class ProjectScanContainer extends ComponentContainer {
     super(globalContainer);
     this.props = props;
     this.components = components;
-    analysisMode = globalContainer.getComponentByType(DefaultAnalysisMode.class);
   }
 
   @Override
@@ -123,6 +120,7 @@ public class ProjectScanContainer extends ComponentContainer {
   private void addBatchComponents() {
     add(
       props,
+      ProjectAnalysisMode.class,
       projectReactorBuilder(),
       new MutableProjectReactorProvider(getComponentByType(ProjectBootstrapper.class)),
       new ImmutableProjectReactorProvider(),
@@ -134,6 +132,7 @@ public class ProjectScanContainer extends ComponentContainer {
       ProjectExclusions.class,
       ProjectReactorValidator.class,
       new ProjectRepositoriesProvider(),
+      new WSLoaderProjectProvider(),
       DefaultResourceCreationLock.class,
       CodeColorizers.class,
       MetricProvider.class,
@@ -206,7 +205,7 @@ public class ProjectScanContainer extends ComponentContainer {
     LOG.debug("Start recursive analysis of project modules");
     DefaultProjectTree tree = getComponentByType(DefaultProjectTree.class);
     scanRecursively(tree.getRootProject());
-    if (analysisMode.isMediumTest()) {
+    if (getComponentByType(ProjectAnalysisMode.class).isMediumTest()) {
       getComponentByType(ScanTaskObservers.class).notifyEndOfScanTask();
     }
   }
index 14720cbc944b9659c3afe4f7fe51e5eb251acbb1..5066ccfc5242409555cc3fe9d004c6974163de6a 100644 (file)
 package org.sonar.batch.scan;
 
 import com.google.common.collect.ImmutableMap;
+
 import java.util.Map;
+
 import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.bootstrap.ProjectReactor;
 import org.sonar.api.config.PropertyDefinitions;
 import org.sonar.api.config.Settings;
 import org.sonar.api.utils.MessageException;
-import org.sonar.batch.bootstrap.DefaultAnalysisMode;
 import org.sonar.batch.bootstrap.DroppedPropertyChecker;
 import org.sonar.batch.bootstrap.GlobalSettings;
 import org.sonar.batch.protocol.input.ProjectRepositories;
@@ -43,10 +44,10 @@ public class ProjectSettings extends Settings {
 
   private final GlobalSettings globalSettings;
   private final ProjectRepositories projectRepositories;
-  private final DefaultAnalysisMode mode;
+  private final ProjectAnalysisMode mode;
 
   public ProjectSettings(ProjectReactor reactor, GlobalSettings globalSettings, PropertyDefinitions propertyDefinitions,
-    ProjectRepositories projectRepositories, DefaultAnalysisMode mode) {
+    ProjectRepositories projectRepositories, ProjectAnalysisMode mode) {
     super(propertyDefinitions);
     this.mode = mode;
     getEncryption().setPathToSecretKey(globalSettings.getString(CoreProperties.ENCRYPTION_SECRET_KEY_PATH));
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/WSLoaderProjectProvider.java b/sonar-batch/src/main/java/org/sonar/batch/scan/WSLoaderProjectProvider.java
new file mode 100644 (file)
index 0000000..164354e
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.batch.scan;
+
+import org.picocontainer.injectors.ProviderAdapter;
+
+import java.util.Map;
+
+import org.sonar.batch.bootstrap.AnalysisProperties;
+import org.sonar.batch.bootstrap.ServerClient;
+import org.sonar.batch.bootstrap.WSLoader;
+import org.sonar.home.cache.PersistentCache;
+import org.sonar.api.batch.AnalysisMode;
+import org.sonar.batch.bootstrap.WSLoader.LoadStrategy;
+
+public class WSLoaderProjectProvider extends ProviderAdapter {
+  private WSLoader wsLoader;
+
+  public WSLoader provide(AnalysisProperties props, AnalysisMode mode, PersistentCache cache, ServerClient client) {
+    if (wsLoader == null) {
+      cache.reconfigure();
+      wsLoader = new WSLoader(isCacheEnabled(props.properties(), mode.isPreview()), cache, client);
+      wsLoader.setStrategy(getStrategy(mode));
+    }
+    return wsLoader;
+  }
+
+  private static LoadStrategy getStrategy(AnalysisMode mode) {
+    if (mode.isQuick()) {
+      return LoadStrategy.CACHE_FIRST;
+    }
+
+    return LoadStrategy.SERVER_FIRST;
+  }
+
+  private static boolean isCacheEnabled(Map<String, String> props, boolean isPreview) {
+    String enableOffline = props.get("sonar.enableOffline");
+    return isPreview && "true".equals(enableOffline);
+  }
+}
index 317612d9f697647d5a1e770737e70c5f64f9b79a..f7296e97de56c3032c45e13f2f0d7b9b74735068 100644 (file)
@@ -19,6 +19,8 @@
  */
 package org.sonar.batch.scan.filesystem;
 
+import org.sonar.batch.scan.ProjectAnalysisMode;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.CoreProperties;
@@ -28,7 +30,6 @@ import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.batch.fs.internal.FileMetadata;
 import org.sonar.api.config.Settings;
 import org.sonar.api.scan.filesystem.PathResolver;
-import org.sonar.batch.bootstrap.DefaultAnalysisMode;
 
 import javax.annotation.CheckForNull;
 
@@ -43,12 +44,12 @@ class InputFileBuilder {
   private final LanguageDetection langDetection;
   private final StatusDetection statusDetection;
   private final DefaultModuleFileSystem fs;
-  private final DefaultAnalysisMode analysisMode;
+  private final ProjectAnalysisMode analysisMode;
   private final Settings settings;
   private final FileMetadata fileMetadata;
 
   InputFileBuilder(String moduleKey, PathResolver pathResolver, LanguageDetection langDetection,
-    StatusDetection statusDetection, DefaultModuleFileSystem fs, DefaultAnalysisMode analysisMode, Settings settings, FileMetadata fileMetadata) {
+    StatusDetection statusDetection, DefaultModuleFileSystem fs, ProjectAnalysisMode analysisMode, Settings settings, FileMetadata fileMetadata) {
     this.moduleKey = moduleKey;
     this.pathResolver = pathResolver;
     this.langDetection = langDetection;
index 304e330e7c1cb4b012e2041cc7445cb3985d8929..3e9d31fbd89fb0d1872327107b6a0f8f64f167e4 100644 (file)
  */
 package org.sonar.batch.scan.filesystem;
 
+import org.sonar.batch.scan.ProjectAnalysisMode;
+
 import org.sonar.api.batch.BatchSide;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.api.batch.fs.internal.FileMetadata;
 import org.sonar.api.config.Settings;
 import org.sonar.api.scan.filesystem.PathResolver;
-import org.sonar.batch.bootstrap.DefaultAnalysisMode;
 
 @BatchSide
 public class InputFileBuilderFactory {
@@ -33,12 +34,12 @@ public class InputFileBuilderFactory {
   private final PathResolver pathResolver;
   private final LanguageDetectionFactory langDetectionFactory;
   private final StatusDetectionFactory statusDetectionFactory;
-  private final DefaultAnalysisMode analysisMode;
+  private final ProjectAnalysisMode analysisMode;
   private final Settings settings;
   private final FileMetadata fileMetadata;
 
   public InputFileBuilderFactory(ProjectDefinition def, PathResolver pathResolver, LanguageDetectionFactory langDetectionFactory,
-    StatusDetectionFactory statusDetectionFactory, DefaultAnalysisMode analysisMode, Settings settings, FileMetadata fileMetadata) {
+    StatusDetectionFactory statusDetectionFactory, ProjectAnalysisMode analysisMode, Settings settings, FileMetadata fileMetadata) {
     this.fileMetadata = fileMetadata;
     this.moduleKey = def.getKeyWithBranch();
     this.pathResolver = pathResolver;
index eaa566bddfda9cf6ea4c0054bcaf4fa1680abf91..9aaf25528c6f8f99b8f7dfb117c0025cec297e25 100644 (file)
  */
 package org.sonar.batch.scan.report;
 
+import org.sonar.batch.scan.ProjectAnalysisMode;
+
 import org.sonar.api.batch.BatchSide;
-import org.sonar.batch.bootstrap.DefaultAnalysisMode;
 
 @BatchSide
 public class IssuesReports {
 
-  private final DefaultAnalysisMode analysisMode;
+  private final ProjectAnalysisMode analysisMode;
   private final Reporter[] reporters;
 
-  public IssuesReports(DefaultAnalysisMode analysisMode, Reporter... reporters) {
+  public IssuesReports(ProjectAnalysisMode analysisMode, Reporter... reporters) {
     this.reporters = reporters;
     this.analysisMode = analysisMode;
   }
index e6321ac8b3b5d68344336469e1b8ec6fb5146b35..b5460a6e2a1aa7fe912affe4883954783a3c034b 100644 (file)
@@ -19,6 +19,9 @@
  */
 package org.sonar.batch.util;
 
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
 import org.apache.commons.lang.StringUtils;
 
 public class BatchUtils {
@@ -34,4 +37,13 @@ public class BatchUtils {
     String cleanKey = StringUtils.deleteWhitespace(projectKey);
     return StringUtils.replace(cleanKey, ":", "_");
   }
+  
+  public static String encodeForUrl(String url) {
+    try {
+      return URLEncoder.encode(url, "UTF-8");
+
+    } catch (UnsupportedEncodingException e) {
+      throw new IllegalStateException("Encoding not supported", e);
+    }
+  }
 }
index c8d2e367475a7706ae8a733d38274506cf192c8b..78995ef7a743f3581abb10b0f25eff2bcf62d5e1 100644 (file)
@@ -50,9 +50,10 @@ public class BatchPluginInstallerTest {
   @Test
   public void listRemotePlugins() {
 
-    ServerClient server = mock(ServerClient.class);
-    when(server.request("/deploy/plugins/index.txt")).thenReturn("checkstyle\nsqale");
-    BatchPluginInstaller installer = new BatchPluginInstaller(server, fileCache, pluginPredicate);
+    WSLoader wsLoader = mock(WSLoader.class);
+    when(wsLoader.load("/deploy/plugins/index.txt")).thenReturn("checkstyle\nsqale".getBytes());
+    when(wsLoader.loadString("/deploy/plugins/index.txt")).thenReturn("checkstyle\nsqale");
+    BatchPluginInstaller installer = new BatchPluginInstaller(wsLoader, fileCache, pluginPredicate);
 
     List<RemotePlugin> remotePlugins = installer.listRemotePlugins();
     assertThat(remotePlugins).extracting("key").containsOnly("checkstyle", "sqale");
@@ -63,8 +64,8 @@ public class BatchPluginInstallerTest {
     File pluginJar = temp.newFile();
     when(fileCache.get(eq("checkstyle-plugin.jar"), eq("fakemd5_1"), any(FileCache.Downloader.class))).thenReturn(pluginJar);
 
-    ServerClient server = mock(ServerClient.class);
-    BatchPluginInstaller installer = new BatchPluginInstaller(server, fileCache, pluginPredicate);
+    WSLoader wsLoader = mock(WSLoader.class);
+    BatchPluginInstaller installer = new BatchPluginInstaller(wsLoader, fileCache, pluginPredicate);
 
     RemotePlugin remote = new RemotePlugin("checkstyle").setFile("checkstyle-plugin.jar", "fakemd5_1");
     File file = installer.download(remote);
@@ -76,9 +77,9 @@ public class BatchPluginInstallerTest {
   public void should_fail_to_get_plugin_index() {
     thrown.expect(IllegalStateException.class);
 
-    ServerClient server = mock(ServerClient.class);
-    doThrow(new IllegalStateException()).when(server).request("/deploy/plugins/index.txt");
+    WSLoader wsLoader = mock(WSLoader.class);
+    doThrow(new IllegalStateException()).when(wsLoader).load("/deploy/plugins/index.txt");
 
-    new BatchPluginInstaller(server, fileCache, pluginPredicate).installRemotes();
+    new BatchPluginInstaller(wsLoader, fileCache, pluginPredicate).installRemotes();
   }
 }
index 511d29d924445ea5abbf5ba0ba071f8d1659a9c0..937281d2fba66a7454413de4b978e7367096d4c7 100644 (file)
@@ -22,7 +22,6 @@ package org.sonar.batch.bootstrap;
 import org.junit.Test;
 import org.sonar.api.CoreProperties;
 import org.sonar.api.config.Settings;
-
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -30,7 +29,7 @@ import static org.mockito.Mockito.when;
 public class BatchPluginPredicateTest {
 
   Settings settings = new Settings();
-  DefaultAnalysisMode mode = mock(DefaultAnalysisMode.class);
+  GlobalMode mode = mock(GlobalMode.class);
 
   @Test
   public void accept_if_no_inclusions_nor_exclusions() {
diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/DefaultAnalysisModeTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/DefaultAnalysisModeTest.java
deleted file mode 100644 (file)
index 65c7d54..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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.batch.bootstrap;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
-import org.junit.Test;
-import org.sonar.api.CoreProperties;
-
-import java.util.Collections;
-import java.util.Map;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class DefaultAnalysisModeTest {
-
-  @Test
-  public void regular_analysis_by_default() {
-    DefaultAnalysisMode mode = new DefaultAnalysisMode(Collections.<String, String>emptyMap());
-
-    assertThat(mode.isPreview()).isFalse();
-    assertThat(mode.isIncremental()).isFalse();
-
-    mode = new DefaultAnalysisMode(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, "pouet"));
-
-    assertThat(mode.isPreview()).isFalse();
-    assertThat(mode.isIncremental()).isFalse();
-  }
-
-  @Test
-  public void support_analysis_mode() {
-    DefaultAnalysisMode mode = new DefaultAnalysisMode(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ANALYSIS));
-
-    assertThat(mode.isPreview()).isFalse();
-    assertThat(mode.isIncremental()).isFalse();
-  }
-
-  @Test
-  public void support_preview_mode() {
-    Map<String, String> props = Maps.newHashMap(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_PREVIEW));
-    DefaultAnalysisMode mode = new DefaultAnalysisMode(props);
-
-    assertThat(mode.isPreview()).isTrue();
-    assertThat(mode.isIncremental()).isFalse();
-
-    assertThat(props.get(CoreProperties.DRY_RUN)).isEqualTo("true");
-  }
-
-  @Test
-  public void support_incremental_mode() {
-    Map<String, String> props = Maps.newHashMap(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_INCREMENTAL));
-    DefaultAnalysisMode mode = new DefaultAnalysisMode(props);
-
-    assertThat(mode.isPreview()).isTrue();
-    assertThat(mode.isIncremental()).isTrue();
-
-    assertThat(props.get(CoreProperties.DRY_RUN)).isEqualTo("true");
-  }
-
-  @Test
-  public void support_deprecated_dryrun_property() {
-    DefaultAnalysisMode mode = new DefaultAnalysisMode(Maps.newHashMap(ImmutableMap.of(CoreProperties.DRY_RUN, "true")));
-
-    assertThat(mode.isPreview()).isTrue();
-    assertThat(mode.isIncremental()).isFalse();
-  }
-}
index b01040b29cd41bc8023d17e143196a71804032ec..aef314012cacb137cdabb464f48e739ce6b1125c 100644 (file)
@@ -40,7 +40,7 @@ import static org.mockito.Mockito.when;
 
 public class ExtensionInstallerTest {
 
-  DefaultAnalysisMode mode;
+  GlobalMode mode;
   BatchPluginRepository pluginRepository = mock(BatchPluginRepository.class);
 
   private static Plugin newPluginInstance(final Object... extensions) {
@@ -53,7 +53,7 @@ public class ExtensionInstallerTest {
 
   @Before
   public void setUp() {
-    mode = mock(DefaultAnalysisMode.class);
+    mode = mock(GlobalMode.class);
   }
 
   @Test
diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/GlobalModeTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/GlobalModeTest.java
new file mode 100644 (file)
index 0000000..fb174d9
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.batch.bootstrap;
+
+import org.sonar.api.CoreProperties;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class GlobalModeTest {
+  @Test
+  public void testQuick() {
+    GlobalMode mode = createMode(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_QUICK);
+    assertThat(mode.isPreview()).isTrue();
+  }
+
+  @Test
+  public void testPreview() {
+    GlobalMode mode = createMode(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_PREVIEW);
+    assertThat(mode.isPreview()).isTrue();
+  }
+
+  @Test
+  public void testIncremental() {
+    GlobalMode mode = createMode(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_INCREMENTAL);
+    assertThat(mode.isPreview()).isTrue();
+  }
+
+  @Test
+  public void testOtherProperty() {
+    GlobalMode mode = createMode(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ANALYSIS);
+    assertThat(mode.isPreview()).isFalse();
+  }
+
+  @Test
+  public void testDeprecatedDryRun() {
+    GlobalMode mode = createMode(CoreProperties.DRY_RUN, "true");
+    assertThat(mode.isPreview()).isTrue();
+  }
+
+  private GlobalMode createMode(String key, String value) {
+    Map<String, String> map = new HashMap<>();
+    map.put(key, value);
+    BootstrapProperties props = new BootstrapProperties(map);
+    return new GlobalMode(props);
+  }
+}
index 18f205ad772f0168980103e4b2f58c990893c4e9..b48ebc78db4d60e2a89b41921119201880b9a05f 100644 (file)
  */
 package org.sonar.batch.bootstrap;
 
+import org.sonar.api.CoreProperties;
+
 import java.util.Collections;
+
+import static org.mockito.Mockito.when;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -28,7 +32,6 @@ import org.sonar.api.config.PropertyDefinitions;
 import org.sonar.api.utils.log.LogTester;
 import org.sonar.api.utils.log.LoggerLevel;
 import org.sonar.batch.protocol.input.GlobalRepositories;
-
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
 
@@ -43,13 +46,13 @@ public class GlobalSettingsTest {
   GlobalRepositories globalRef;
   BootstrapProperties bootstrapProps;
 
-  private DefaultAnalysisMode mode;
+  private GlobalMode mode;
 
   @Before
   public void prepare() {
     globalRef = new GlobalRepositories();
     bootstrapProps = new BootstrapProperties(Collections.<String, String>emptyMap());
-    mode = mock(DefaultAnalysisMode.class);
+    mode = mock(GlobalMode.class);
   }
 
   @Test
@@ -60,6 +63,13 @@ public class GlobalSettingsTest {
 
     assertThat(batchSettings.getBoolean("sonar.cpd.cross")).isTrue();
   }
+  
+  @Test
+  public void support_deprecated_dry_run() {
+    when(mode.isPreview()).thenReturn(true);
+    GlobalSettings batchSettings = new GlobalSettings(bootstrapProps, new PropertyDefinitions(), globalRef, mode);
+    assertThat(batchSettings.getString(CoreProperties.DRY_RUN)).isEqualTo("true");
+  }
 
   @Test
   public void should_log_warn_msg_for_each_jdbc_property_if_present() {
diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/MockHttpServer.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/MockHttpServer.java
new file mode 100644 (file)
index 0000000..757e353
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.batch.bootstrap;
+
+import org.apache.commons.io.IOUtils;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+
+import static javax.servlet.http.HttpServletResponse.SC_OK;
+import static org.apache.commons.io.IOUtils.write;
+
+public class MockHttpServer {
+  private Server server;
+  private String responseBody;
+  private String requestBody;
+  private String mockResponseData;
+  private int mockResponseStatus = SC_OK;
+  private int numRequests = 0;
+
+  public void start() throws Exception {
+    server = new Server(0);
+    server.setHandler(getMockHandler());
+    server.start();
+  }
+
+  public int getNumberRequests() {
+    return numRequests;
+  }
+
+  /**
+   * Creates an {@link org.mortbay.jetty.handler.AbstractHandler handler} returning an arbitrary String as a response.
+   *
+   * @return never <code>null</code>.
+   */
+  public Handler getMockHandler() {
+    Handler handler = new AbstractHandler() {
+
+      public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
+        numRequests++;
+        setResponseBody(getMockResponseData());
+        setRequestBody(IOUtils.toString(baseRequest.getInputStream()));
+        response.setStatus(mockResponseStatus);
+        response.setContentType("text/xml;charset=utf-8");
+        write(getResponseBody(), response.getOutputStream());
+        baseRequest.setHandled(true);
+      }
+    };
+    return handler;
+  }
+
+  public void stop() {
+    try {
+      if (server != null) {
+        server.stop();
+      }
+    } catch (Exception e) {
+      throw new IllegalStateException("Fail to stop HTTP server", e);
+    }
+  }
+
+  public String getResponseBody() {
+    return responseBody;
+  }
+
+  public void setResponseBody(String responseBody) {
+    this.responseBody = responseBody;
+  }
+
+  public String getRequestBody() {
+    return requestBody;
+  }
+
+  public void setRequestBody(String requestBody) {
+    this.requestBody = requestBody;
+  }
+
+  public void setMockResponseStatus(int status) {
+    this.mockResponseStatus = status;
+  }
+
+  public String getMockResponseData() {
+    return mockResponseData;
+  }
+
+  public void setMockResponseData(String mockResponseData) {
+    this.mockResponseData = mockResponseData;
+  }
+
+  public int getPort() {
+    return server.getConnectors()[0].getLocalPort();
+  }
+
+}
index 813c5efce7045336c781abb006cf477692ea20f8..4857621a9102b647e420d3030ec5116928b17e9c 100644 (file)
@@ -45,19 +45,4 @@ public class PersistentCacheProviderTest {
   public void test_cache_dir() {
     assertThat(provider.provide(props).getBaseDirectory().toFile()).exists().isDirectory();
   }
-
-  @Test
-  public void test_enableCache() {
-    // normally force update (cache disabled)
-    assertThat(provider.provide(props).isForceUpdate()).isTrue();
-
-    props.properties().put("sonar.enableHttpCache", "true");
-    provider = new PersistentCacheProvider();
-    assertThat(provider.provide(props).isForceUpdate()).isFalse();
-  }
-  
-  @Test
-  public void test_reconfigure() {
-    
-  }
 }
index 75538ff5b7377f3c730062ddc95309bf45f50876..56133fe40d0bc9f3d79b66bbd0a3a220019e7f34 100644 (file)
  */
 package org.sonar.batch.bootstrap;
 
+import org.sonar.batch.util.BatchUtils;
+
 import java.io.File;
-import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.apache.commons.io.IOUtils;
-import org.eclipse.jetty.server.Handler;
-import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.handler.AbstractHandler;
+
 import org.junit.After;
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.junit.rules.TemporaryFolder;
 import org.sonar.batch.bootstrapper.EnvironmentInformation;
-import org.sonar.home.cache.PersistentCache;
-import org.sonar.home.cache.PersistentCacheBuilder;
-
-import static javax.servlet.http.HttpServletResponse.SC_OK;
-import static org.apache.commons.io.IOUtils.write;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
@@ -53,19 +41,10 @@ public class ServerClientTest {
   @Rule
   public TemporaryFolder temp = new TemporaryFolder();
   @Rule
-  public TemporaryFolder cacheTmp = new TemporaryFolder();
-  @Rule
   public ExpectedException thrown = ExpectedException.none();
 
   private MockHttpServer server = null;
   private BootstrapProperties bootstrapProps = mock(BootstrapProperties.class);
-  private DefaultAnalysisMode mode = null;
-
-  @Before
-  public void setUp() {
-    mode = mock(DefaultAnalysisMode.class);
-    when(mode.isPreview()).thenReturn(true);
-  }
 
   @After
   public void stopServer() {
@@ -74,78 +53,30 @@ public class ServerClientTest {
     }
   }
 
-  @Test
-  public void dont_cache_post_request() throws Exception {
-    server = new MockHttpServer();
-    server.start();
-    server.setMockResponseData("this is the content");
-
-    assertThat(newServerClient().request("/foo", "POST")).isEqualTo("this is the content");
-
-    // cache never accessed, so not even the .lock should be there
-    assertThat(getNumFilesInCache()).isEqualTo(0);
-  }
-
-  @Test
-  public void dont_cache_non_preview_mode() throws Exception {
-    server = new MockHttpServer();
-    server.start();
-    server.setMockResponseData("this is the content");
-
-    when(mode.isPreview()).thenReturn(false);
-    assertThat(newServerClient().request("/foo")).isEqualTo("this is the content");
-
-    // cache never accessed, so not even the .lock should be there
-    assertThat(getNumFilesInCache()).isEqualTo(0);
-  }
-
-  @Test
-  public void cache_preview_mode() throws Exception {
-    server = new MockHttpServer();
-    server.start();
-    server.setMockResponseData("this is the content");
-
-    assertThat(newServerClient().request("/foo")).isEqualTo("this is the content");
-
-    // should have the .lock and one request cached
-    assertThat(getNumFilesInCache()).isEqualTo(2);
-  }
-
   @Test
   public void should_remove_url_ending_slash() {
     BootstrapProperties settings = mock(BootstrapProperties.class);
     when(settings.property("sonar.host.url")).thenReturn("http://localhost:8080/sonar/");
-
-    PersistentCache ps = new PersistentCacheBuilder(new Slf4jLogger()).setSonarHome(cacheTmp.getRoot().toPath()).build();
-    ServerClient client = new ServerClient(settings, new EnvironmentInformation("Junit", "4"), ps, mode);
+    ServerClient client = new ServerClient(settings, new EnvironmentInformation("Junit", "4"));
 
     assertThat(client.getURL()).isEqualTo("http://localhost:8080/sonar");
   }
 
   @Test
   public void should_request_url() throws Exception {
-    server = new MockHttpServer();
-    server.start();
-    server.setMockResponseData("this is the content");
-
-    assertThat(newServerClient().request("/foo")).isEqualTo("this is the content");
+    startServer(null, "this is the content");
+    assertThat(newServerClient().downloadString("/foo")).isEqualTo("this is the content");
   }
 
   @Test
   public void should_escape_html_from_url() throws Exception {
-    server = new MockHttpServer();
-    server.start();
-    server.setMockResponseData("this is the content");
-
-    assertThat(newServerClient().request("/<foo>")).isEqualTo("this is the content");
+    startServer(null, "this is the content");
+    assertThat(newServerClient().downloadString("/<foo>")).isEqualTo("this is the content");
   }
 
   @Test
   public void should_download_file() throws Exception {
-    server = new MockHttpServer();
-    server.start();
-    server.setMockResponseData("this is the content");
-
+    startServer(null, "this is the content");
     File file = temp.newFile();
     newServerClient().download("/foo", file);
     assertThat(new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8)).isEqualTo("this is the content");
@@ -153,138 +84,55 @@ public class ServerClientTest {
 
   @Test
   public void should_fail_if_unauthorized_with_no_login_password() throws Exception {
-    server = new MockHttpServer();
-    server.start();
-    server.setMockResponseStatus(401);
-
+    startServer(401, null);
     thrown.expectMessage("Not authorized. Analyzing this project requires to be authenticated. Please provide the values of the properties sonar.login and sonar.password.");
-    newServerClient().request("/foo");
+    newServerClient().downloadString("/foo");
   }
 
   @Test
   public void should_fail_if_unauthorized_with_login_password_provided() throws Exception {
-    server = new MockHttpServer();
-    server.start();
-    server.setMockResponseStatus(401);
+    startServer(401, null);
 
     when(bootstrapProps.property(eq("sonar.login"))).thenReturn("login");
     when(bootstrapProps.property(eq("sonar.password"))).thenReturn("password");
 
     thrown.expectMessage("Not authorized. Please check the properties sonar.login and sonar.password");
-    newServerClient().request("/foo");
+    newServerClient().downloadString("/foo");
   }
 
   @Test
   public void should_display_json_error_when_403() throws Exception {
-    server = new MockHttpServer();
-    server.start();
-    server.setMockResponseStatus(403);
-    server.setMockResponseData("{\"errors\":[{\"msg\":\"Insufficient privileges\"}]}");
-
+    startServer(403, "{\"errors\":[{\"msg\":\"Insufficient privileges\"}]}");
     thrown.expectMessage("Insufficient privileges");
-    newServerClient().request("/foo");
+    newServerClient().downloadString("/foo");
   }
 
   @Test
   public void should_fail_if_error() throws Exception {
-    server = new MockHttpServer();
-    server.start();
-    server.setMockResponseStatus(500);
-
+    startServer(500, null);
     thrown.expectMessage("Fail to execute request [code=500, url=http://localhost:" + server.getPort() + "/foo]");
-    newServerClient().request("/foo");
+    newServerClient().downloadString("/foo");
   }
 
   @Test
-  public void testEncode() {
-    assertThat(ServerClient.encodeForUrl("my value")).isEqualTo("my+value");
+  public void string_encode() {
+    assertThat(BatchUtils.encodeForUrl("my value")).isEqualTo("my+value");
   }
 
   private ServerClient newServerClient() {
     when(bootstrapProps.property("sonar.host.url")).thenReturn("http://localhost:" + server.getPort());
-    PersistentCache ps = new PersistentCacheBuilder(new Slf4jLogger()).setSonarHome(cacheTmp.getRoot().toPath()).build();
-    return new ServerClient(bootstrapProps, new EnvironmentInformation("Junit", "4"), ps, mode);
+    return new ServerClient(bootstrapProps, new EnvironmentInformation("Junit", "4"));
   }
 
-  private int getNumFilesInCache() {
-    return new File(cacheTmp.getRoot(), "ws_cache").listFiles().length;
-  }
-
-  static class MockHttpServer {
-    private Server server;
-    private String responseBody;
-    private String requestBody;
-    private String mockResponseData;
-    private int mockResponseStatus = SC_OK;
-
-    public void start() throws Exception {
-      server = new Server(0);
-      server.setHandler(getMockHandler());
-      server.start();
-    }
-
-    /**
-     * Creates an {@link org.mortbay.jetty.handler.AbstractHandler handler} returning an arbitrary String as a response.
-     *
-     * @return never <code>null</code>.
-     */
-    public Handler getMockHandler() {
-      Handler handler = new AbstractHandler() {
-
-        public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
-          setResponseBody(getMockResponseData());
-          setRequestBody(IOUtils.toString(baseRequest.getInputStream()));
-          response.setStatus(mockResponseStatus);
-          response.setContentType("text/xml;charset=utf-8");
-          write(getResponseBody(), response.getOutputStream());
-          baseRequest.setHandled(true);
-        }
-      };
-      return handler;
-    }
-
-    public void stop() {
-      try {
-        if (server != null) {
-          server.stop();
-        }
-      } catch (Exception e) {
-        throw new IllegalStateException("Fail to stop HTTP server", e);
-      }
-    }
-
-    public String getResponseBody() {
-      return responseBody;
-    }
-
-    public void setResponseBody(String responseBody) {
-      this.responseBody = responseBody;
-    }
-
-    public String getRequestBody() {
-      return requestBody;
-    }
-
-    public void setRequestBody(String requestBody) {
-      this.requestBody = requestBody;
-    }
-
-    public void setMockResponseStatus(int status) {
-      this.mockResponseStatus = status;
-    }
-
-    public String getMockResponseData() {
-      return mockResponseData;
-    }
-
-    public void setMockResponseData(String mockResponseData) {
-      this.mockResponseData = mockResponseData;
+  private void startServer(Integer responseStatus, String responseData) throws Exception {
+    server = new MockHttpServer();
+    server.start();
+    
+    if (responseStatus != null) {
+      server.setMockResponseStatus(responseStatus);
     }
-
-    public int getPort() {
-      return server.getConnectors()[0].getLocalPort();
+    if (responseData != null) {
+      server.setMockResponseData(responseData);
     }
-
   }
-
 }
diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/WSLoaderGlobalProviderTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/WSLoaderGlobalProviderTest.java
new file mode 100644 (file)
index 0000000..8cc8b11
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.batch.bootstrap;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.mockito.Mockito.when;
+
+import org.sonar.batch.bootstrap.WSLoader.LoadStrategy;
+import org.junit.Test;
+import org.junit.Before;
+import org.sonar.home.cache.PersistentCache;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class WSLoaderGlobalProviderTest {
+  @Mock
+  private PersistentCache cache;
+
+  @Mock
+  private ServerClient client;
+
+  @Mock
+  private GlobalMode mode;
+
+  private WSLoaderGlobalProvider loaderProvider;
+  private Map<String, String> propMap;
+  private BootstrapProperties props;
+
+  @Before
+  public void setUp() {
+    MockitoAnnotations.initMocks(this);
+    loaderProvider = new WSLoaderGlobalProvider();
+  }
+
+  @Test
+  public void testDefault() {
+    propMap = new HashMap<>();
+    props = new BootstrapProperties(propMap);
+
+    WSLoader wsLoader = loaderProvider.provide(props, mode, cache, client);
+    assertThat(wsLoader.getStrategy()).isEqualTo(LoadStrategy.SERVER_FIRST);
+    assertThat(wsLoader.isCacheEnabled()).isEqualTo(false);
+  }
+
+  @Test
+  public void testOffline() {
+    propMap = new HashMap<>();
+    propMap.put("sonar.enableOffline", "true");
+    when(mode.isPreview()).thenReturn(true);
+    props = new BootstrapProperties(propMap);
+
+    WSLoader wsLoader = loaderProvider.provide(props, mode, cache, client);
+    assertThat(wsLoader.isCacheEnabled()).isEqualTo(true);
+  }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/WSLoaderTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/WSLoaderTest.java
new file mode 100644 (file)
index 0000000..c4571c9
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.batch.bootstrap;
+
+import org.sonar.api.utils.HttpDownloader;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.apache.commons.io.IOUtils;
+import org.mockito.Mockito;
+import org.mockito.InOrder;
+
+import java.io.IOException;
+import java.net.URI;
+
+import static org.mockito.Mockito.mock;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.anyInt;
+import org.sonar.batch.bootstrap.WSLoader.LoadStrategy;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.junit.Before;
+import org.sonar.home.cache.PersistentCache;
+import org.mockito.Mock;
+
+public class WSLoaderTest {
+  private final static String ID = "/dummy";
+  private final static String cacheValue = "cache";
+  private final static String serverValue = "server";
+
+  @Mock
+  private ServerClient client;
+  @Mock
+  private PersistentCache cache;
+
+  @Before
+  public void setUp() throws IOException {
+    MockitoAnnotations.initMocks(this);
+    when(client.load(anyString(), anyString(), anyBoolean(), anyInt(), anyInt())).thenReturn(IOUtils.toInputStream(serverValue));
+    when(cache.get(ID, null)).thenReturn(cacheValue.getBytes());
+    when(client.getURI(anyString())).thenAnswer(new Answer<URI>() {
+      @Override
+      public URI answer(InvocationOnMock invocation) throws Throwable {
+        return new URI((String) invocation.getArguments()[0]);
+      }
+    });
+  }
+
+  @Test
+  public void dont_retry_server() throws IOException {
+    when(client.load(anyString(), anyString(), anyBoolean(), anyInt(), anyInt())).thenThrow(new IllegalStateException());
+    WSLoader loader = new WSLoader(true, cache, client);
+    loader.setStrategy(LoadStrategy.SERVER_FIRST);
+    loader.setCacheEnabled(true);
+
+    assertThat(loader.loadString(ID)).isEqualTo(cacheValue);
+    assertThat(loader.loadString(ID)).isEqualTo(cacheValue);
+
+    // only try once the server
+    verify(client, times(1)).load(anyString(), anyString(), anyBoolean(), anyInt(), anyInt());
+    verify(cache, times(2)).get(ID, null);
+  }
+
+  @Test
+  public void test_cache_strategy_fallback() throws IOException {
+    when(cache.get(ID, null)).thenReturn(null);
+    WSLoader loader = new WSLoader(true, cache, client);
+    loader.setStrategy(LoadStrategy.CACHE_FIRST);
+    loader.setCacheEnabled(true);
+
+    loader.load(ID);
+
+    InOrder inOrder = Mockito.inOrder(client, cache);
+    inOrder.verify(cache).get(ID, null);
+    inOrder.verify(client).load(eq(ID), anyString(), anyBoolean(), anyInt(), anyInt());
+  }
+
+  @Test
+  public void test_server_strategy_fallback() throws IOException {
+    when(client.load(anyString(), anyString(), anyBoolean(), anyInt(), anyInt())).thenThrow(new IllegalStateException());
+    WSLoader loader = new WSLoader(true, cache, client);
+    loader.setStrategy(LoadStrategy.SERVER_FIRST);
+    loader.setCacheEnabled(true);
+    assertThat(loader.loadString(ID)).isEqualTo(cacheValue);
+
+    InOrder inOrder = Mockito.inOrder(client, cache);
+    inOrder.verify(client).load(eq(ID), anyString(), anyBoolean(), anyInt(), anyInt());
+    inOrder.verify(cache).get(ID, null);
+  }
+
+  @Test
+  public void test_put_cache() throws IOException {
+    WSLoader loader = new WSLoader(true, cache, client);
+    loader.setStrategy(LoadStrategy.SERVER_FIRST);
+    loader.load(ID);
+    verify(cache).put(ID, serverValue.getBytes());
+  }
+
+  @Test(expected = NullPointerException.class)
+  public void test_throw_cache_exception_fallback() throws IOException {
+    when(client.load(anyString(), anyString(), anyBoolean(), anyInt(), anyInt())).thenThrow(new IllegalStateException());
+    when(cache.get(ID, null)).thenThrow(new NullPointerException());
+    WSLoader loader = new WSLoader(true, cache, client);
+    loader.setStrategy(LoadStrategy.SERVER_FIRST);
+    loader.setCacheEnabled(true);
+
+    loader.load(ID);
+  }
+
+  @Test(expected = IllegalStateException.class)
+  public void test_throw_cache_exception() throws IOException {
+    when(cache.get(ID, null)).thenThrow(new IllegalStateException());
+    WSLoader loader = new WSLoader(true, cache, client);
+    loader.setStrategy(LoadStrategy.CACHE_FIRST);
+    loader.setCacheEnabled(true);
+
+    loader.load(ID);
+  }
+
+  @Test(expected = IllegalStateException.class)
+  public void test_throw_http_exceptions() {
+    HttpDownloader.HttpException httpException = mock(HttpDownloader.HttpException.class);
+    IllegalStateException wrapperException = new IllegalStateException(httpException);
+    
+    when(client.load(anyString(), anyString(), anyBoolean(), anyInt(), anyInt())).thenThrow(wrapperException);
+    
+    WSLoader loader = new WSLoader(true, cache, client);
+    loader.setStrategy(LoadStrategy.SERVER_FIRST);
+    
+    try {
+      loader.load(ID);
+    } catch(IllegalStateException e) {
+      // cache should not be used
+      verifyNoMoreInteractions(cache);
+      throw e;
+    }
+  }
+
+  @Test
+  public void test_server_not_accessible() throws IOException {
+    when(client.load(anyString(), anyString(), anyBoolean(), anyInt(), anyInt())).thenThrow(new IllegalStateException());
+    WSLoader loader = new WSLoader(true, cache, client);
+    loader.setStrategy(LoadStrategy.SERVER_FIRST);
+    loader.load(ID);
+    loader.load(ID);
+
+    // only try once from server
+    verify(client, times(1)).load(eq(ID), anyString(), anyBoolean(), anyInt(), anyInt());
+    verify(cache, times(2)).get(ID, null);
+  }
+
+  @Test
+  public void test_change_strategy() throws IOException {
+    WSLoader loader = new WSLoader(true, cache, client);
+    loader.setStrategy(LoadStrategy.CACHE_FIRST);
+    test_cache_strategy_fallback();
+  }
+
+  @Test
+  public void test_enable_cache() throws IOException {
+    WSLoader loader = new WSLoader(true, cache, client);
+    loader.setCacheEnabled(false);
+    test_cache_disabled();
+  }
+
+  @Test
+  public void test_server_strategy() throws IOException {
+    WSLoader loader = new WSLoader(true, cache, client);
+    loader.setStrategy(LoadStrategy.SERVER_FIRST);
+    loader.load(ID);
+
+    // should not fetch from cache
+    verify(cache).put(ID, serverValue.getBytes());
+    verifyNoMoreInteractions(cache);
+  }
+
+  @Test
+  public void test_cache_disabled() throws IOException {
+    WSLoader loader = new WSLoader(cache, client);
+    loader.load(ID);
+
+    // should not even put
+    verifyNoMoreInteractions(cache);
+  }
+
+  @Test
+  public void test_string() {
+    WSLoader loader = new WSLoader(cache, client);
+    assertThat(loader.loadString(ID)).isEqualTo(serverValue);
+  }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/WSLoaderTestWithServer.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/WSLoaderTestWithServer.java
new file mode 100644 (file)
index 0000000..5b5f2c7
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.batch.bootstrap;
+
+import static org.mockito.Mockito.mock;
+import org.sonar.batch.bootstrap.WSLoader.LoadStrategy;
+import org.junit.Rule;
+import org.junit.rules.TemporaryFolder;
+
+import static org.mockito.Mockito.when;
+import static org.assertj.core.api.Assertions.assertThat;
+import org.sonar.batch.bootstrapper.EnvironmentInformation;
+import org.sonar.home.cache.PersistentCache;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class WSLoaderTestWithServer {
+  private static final String RESPONSE_STRING = "this is the content";
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
+  private MockHttpServer server;
+  private PersistentCache cache;
+  private ServerClient client;
+  private WSLoader loader;
+
+  @Before
+  public void setUp() throws Exception {
+    server = new MockHttpServer();
+    server.start();
+
+    BootstrapProperties bootstrapProps = mock(BootstrapProperties.class);
+    when(bootstrapProps.property("sonar.host.url")).thenReturn("http://localhost:" + server.getPort());
+
+    client = new ServerClient(bootstrapProps, new EnvironmentInformation("Junit", "4"));
+    cache = new PersistentCache(temp.getRoot().toPath(), 1000 * 60, new Slf4jLogger());
+    loader = new WSLoader(cache, client);
+  }
+
+  @After
+  public void tearDown() {
+    if (server != null) {
+      server.stop();
+    }
+  }
+
+  @Test
+  public void testServer() {
+    loader.setCacheEnabled(false);
+    loader.setStrategy(LoadStrategy.SERVER_FIRST);
+    server.setMockResponseData(RESPONSE_STRING);
+    assertThat(loader.loadString("/foo")).isEqualTo(RESPONSE_STRING);
+  }
+
+  @Test
+  public void testCacheDisabled() {
+    loader.setCacheEnabled(false);
+    loader.setStrategy(LoadStrategy.CACHE_FIRST);
+    makeRequests();
+    assertThat(server.getNumberRequests()).isEqualTo(3);
+  }
+
+  @Test
+  public void testCacheEnabled() {
+    loader.setCacheEnabled(true);
+    loader.setStrategy(LoadStrategy.CACHE_FIRST);
+    makeRequests();
+    assertThat(server.getNumberRequests()).isEqualTo(1);
+  }
+
+  @Test
+  public void testServerStrategy() {
+    loader.setCacheEnabled(true);
+    loader.setStrategy(LoadStrategy.SERVER_FIRST);
+    makeRequests();
+    assertThat(server.getNumberRequests()).isEqualTo(3);
+  }
+
+  @Test
+  public void testCacheStrategyDisabled() {
+    loader.setCacheEnabled(false);
+    loader.setStrategy(LoadStrategy.CACHE_FIRST);
+    makeRequests();
+    assertThat(server.getNumberRequests()).isEqualTo(3);
+  }
+
+  private void makeRequests() {
+    server.setMockResponseData(RESPONSE_STRING);
+    assertThat(loader.loadString("/foo")).isEqualTo(RESPONSE_STRING);
+    assertThat(loader.loadString("/foo")).isEqualTo(RESPONSE_STRING);
+    assertThat(loader.loadString("/foo")).isEqualTo(RESPONSE_STRING);
+  }
+
+}
index 07279e5396b676d92cc7c31a1f6fff329029c8dd..af5a763c26d504a5e8ec2081810c7e3c2a11491d 100644 (file)
  */
 package org.sonar.batch.cpd.index;
 
+import org.sonar.batch.scan.ProjectAnalysisMode;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.slf4j.Logger;
 import org.sonar.api.CoreProperties;
 import org.sonar.api.config.Settings;
 import org.sonar.api.resources.Project;
-import org.sonar.batch.bootstrap.DefaultAnalysisMode;
-
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -38,13 +38,13 @@ public class IndexFactoryTest {
   Settings settings;
   IndexFactory factory;
   Logger logger;
-  private DefaultAnalysisMode analysisMode;
+  private ProjectAnalysisMode analysisMode;
 
   @Before
   public void setUp() {
     project = new Project("foo");
     settings = new Settings();
-    analysisMode = mock(DefaultAnalysisMode.class);
+    analysisMode = mock(ProjectAnalysisMode.class);
     factory = new IndexFactory(analysisMode, settings);
     logger = mock(Logger.class);
   }
index 28d9b251ec9baa190022d746e4773185148c0290..991e61dc4635e9e3b271ad895d2423ee3e1bd7c1 100644 (file)
  */
 package org.sonar.batch.issue.tracking;
 
+import org.sonar.batch.bootstrap.WSLoader;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.sonar.api.utils.HttpDownloader;
-import org.sonar.batch.bootstrap.ServerClient;
 
 import java.net.URI;
 import java.net.URISyntaxException;
@@ -46,32 +47,32 @@ public class DefaultServerLineHashesLoaderTest {
 
   @Test
   public void should_download_source_from_ws_if_preview_mode() {
-    ServerClient server = mock(ServerClient.class);
-    when(server.request(anyString())).thenReturn("ae12\n\n43fb");
+    WSLoader wsLoader = mock(WSLoader.class);
+    when(wsLoader.loadString(anyString())).thenReturn("ae12\n\n43fb");
 
-    ServerLineHashesLoader lastSnapshots = new DefaultServerLineHashesLoader(server);
+    ServerLineHashesLoader lastSnapshots = new DefaultServerLineHashesLoader(wsLoader);
 
     String[] hashes = lastSnapshots.getLineHashes("myproject:org/foo/Bar.c");
     assertThat(hashes).containsOnly("ae12", "", "43fb");
-    verify(server).request("/api/sources/hash?key=myproject%3Aorg%2Ffoo%2FBar.c");
+    verify(wsLoader).loadString("/api/sources/hash?key=myproject%3Aorg%2Ffoo%2FBar.c");
   }
 
   @Test
   public void should_download_source_with_space_from_ws_if_preview_mode() {
-    ServerClient server = mock(ServerClient.class);
-    when(server.request(anyString())).thenReturn("ae12\n\n43fb");
+    WSLoader server = mock(WSLoader.class);
+    when(server.loadString(anyString())).thenReturn("ae12\n\n43fb");
 
     ServerLineHashesLoader lastSnapshots = new DefaultServerLineHashesLoader(server);
 
     String[] hashes = lastSnapshots.getLineHashes("myproject:org/foo/Foo Bar.c");
     assertThat(hashes).containsOnly("ae12", "", "43fb");
-    verify(server).request("/api/sources/hash?key=myproject%3Aorg%2Ffoo%2FFoo+Bar.c");
+    verify(server).loadString("/api/sources/hash?key=myproject%3Aorg%2Ffoo%2FFoo+Bar.c");
   }
 
   @Test
   public void should_fail_to_download_source_from_ws() throws URISyntaxException {
-    ServerClient server = mock(ServerClient.class);
-    when(server.request(anyString())).thenThrow(new HttpDownloader.HttpException(new URI(""), 500));
+    WSLoader server = mock(WSLoader.class);
+    when(server.loadString(anyString())).thenThrow(new HttpDownloader.HttpException(new URI(""), 500));
 
     ServerLineHashesLoader lastSnapshots = new DefaultServerLineHashesLoader(server);
 
index ea301e425b0235d42b43daa3075dcf73f4be0453..df097d684572bc2003ce1935085946c382e87586 100644 (file)
@@ -19,6 +19,8 @@
  */
 package org.sonar.batch.report;
 
+import org.sonar.batch.scan.ProjectAnalysisMode;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.slf4j.Logger;
@@ -27,23 +29,21 @@ import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.api.config.Settings;
 import org.sonar.api.platform.Server;
 import org.sonar.api.utils.TempFolder;
-import org.sonar.batch.bootstrap.DefaultAnalysisMode;
 import org.sonar.batch.bootstrap.ServerClient;
 import org.sonar.batch.scan.ImmutableProjectReactor;
-
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 public class ReportPublisherTest {
 
-  private DefaultAnalysisMode mode;
+  private ProjectAnalysisMode mode;
 
   private ImmutableProjectReactor reactor;
 
   @Before
   public void setUp() {
-    mode = mock(DefaultAnalysisMode.class);
+    mode = mock(ProjectAnalysisMode.class);
     reactor = mock(ImmutableProjectReactor.class);
     when(reactor.getRoot()).thenReturn(ProjectDefinition.create().setKey("struts"));
   }
index 9458bc31c35f4ad271a989757c97a999bc280b2d..c9cadbe05eef04b7389efa69f9e03d87c26dee56 100644 (file)
  */
 package org.sonar.batch.repository;
 
+import org.sonar.batch.scan.ProjectAnalysisMode;
+
+import org.sonar.batch.bootstrap.WSLoader;
 import com.google.common.collect.Maps;
 import org.junit.Before;
 import org.junit.Test;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.api.batch.bootstrap.ProjectReactor;
 import org.sonar.batch.bootstrap.AnalysisProperties;
-import org.sonar.batch.bootstrap.DefaultAnalysisMode;
-import org.sonar.batch.bootstrap.ServerClient;
 import org.sonar.batch.rule.ModuleQProfiles;
-
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
@@ -38,18 +38,18 @@ import static org.mockito.Mockito.when;
 public class DefaultProjectRepositoriesLoaderTest {
 
   private DefaultProjectRepositoriesLoader loader;
-  private ServerClient serverClient;
-  private DefaultAnalysisMode analysisMode;
+  private WSLoader wsLoader;
+  private ProjectAnalysisMode analysisMode;
   private ProjectReactor reactor;
   private AnalysisProperties taskProperties;
 
   @Before
   public void prepare() {
-    serverClient = mock(ServerClient.class);
-    analysisMode = mock(DefaultAnalysisMode.class);
-    loader = new DefaultProjectRepositoriesLoader(serverClient, analysisMode);
+    wsLoader = mock(WSLoader.class);
+    analysisMode = mock(ProjectAnalysisMode.class);
+    loader = new DefaultProjectRepositoriesLoader(wsLoader, analysisMode);
     loader = spy(loader);
-    when(serverClient.request(anyString())).thenReturn("{}");
+    when(wsLoader.loadString(anyString())).thenReturn("{}");
     taskProperties = new AnalysisProperties(Maps.<String, String>newHashMap(), "");
   }
 
@@ -58,18 +58,18 @@ public class DefaultProjectRepositoriesLoaderTest {
     reactor = new ProjectReactor(ProjectDefinition.create().setKey("foo"));
     when(analysisMode.isPreview()).thenReturn(false);
     loader.load(reactor, taskProperties);
-    verify(serverClient).request("/batch/project?key=foo&preview=false");
+    verify(wsLoader).loadString("/batch/project?key=foo&preview=false");
 
     when(analysisMode.isPreview()).thenReturn(true);
     loader.load(reactor, taskProperties);
-    verify(serverClient).request("/batch/project?key=foo&preview=true");
+    verify(wsLoader).loadString("/batch/project?key=foo&preview=true");
   }
 
   @Test
   public void passAndEncodeProjectKeyParameter() {
     reactor = new ProjectReactor(ProjectDefinition.create().setKey("foo bàr"));
     loader.load(reactor, taskProperties);
-    verify(serverClient).request("/batch/project?key=foo+b%C3%A0r&preview=false");
+    verify(wsLoader).loadString("/batch/project?key=foo+b%C3%A0r&preview=false");
   }
 
   @Test
@@ -77,7 +77,7 @@ public class DefaultProjectRepositoriesLoaderTest {
     reactor = new ProjectReactor(ProjectDefinition.create().setKey("foo"));
     taskProperties.properties().put(ModuleQProfiles.SONAR_PROFILE_PROP, "my-profile#2");
     loader.load(reactor, taskProperties);
-    verify(serverClient).request("/batch/project?key=foo&profile=my-profile%232&preview=false");
+    verify(wsLoader).loadString("/batch/project?key=foo&profile=my-profile%232&preview=false");
   }
 
 }
index 4cc756ff1ce0253da66d18328f791137987ca4b4..d1686d63372a56118ba7c9ff12d5669762c4ca4c 100644 (file)
  */
 package org.sonar.batch.repository;
 
+import com.google.common.io.ByteSource;
+
+import org.sonar.batch.bootstrap.WSLoader;
 import com.google.common.base.Function;
-import com.google.common.io.InputSupplier;
 import org.junit.Before;
 import org.junit.Test;
-import org.sonar.batch.bootstrap.ServerClient;
 import org.sonar.batch.protocol.input.BatchInput;
 import org.sonar.batch.protocol.input.BatchInput.ServerIssue;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -39,18 +39,18 @@ import static org.mockito.Mockito.when;
 
 public class DefaultServerIssuesLoaderTest {
   private DefaultServerIssuesLoader loader;
-  private ServerClient serverClient;
+  private WSLoader wsLoader;
 
   @Before
   public void prepare() {
-    serverClient = mock(ServerClient.class);
-    loader = new DefaultServerIssuesLoader(serverClient);
+    wsLoader = mock(WSLoader.class);
+    loader = new DefaultServerIssuesLoader(wsLoader);
   }
 
   @Test
   public void loadFromWs() throws Exception {
-    InputSupplier<InputStream> is = mock(InputSupplier.class);
-    when(serverClient.doRequest("/batch/issues?key=foo", "GET", null)).thenReturn(is);
+    ByteSource bs = mock(ByteSource.class);
+    when(wsLoader.loadSource("/batch/issues?key=foo")).thenReturn(bs);
 
     ByteArrayOutputStream bos = new ByteArrayOutputStream();
 
@@ -59,7 +59,7 @@ public class DefaultServerIssuesLoaderTest {
     ServerIssue.newBuilder().setKey("ab2").build()
       .writeDelimitedTo(bos);
 
-    when(is.getInput()).thenReturn(new ByteArrayInputStream(bos.toByteArray()));
+    when(bs.openStream()).thenReturn(new ByteArrayInputStream(bos.toByteArray()));
 
     final List<ServerIssue> result = new ArrayList<>();
     loader.load("foo", new Function<BatchInput.ServerIssue, Void>() {
index 54d62bce5f84c8813dd4323bbed4b91f48e0255c..6910f2dc4620046e8a5eb2836bc9dc8acc0140e2 100644 (file)
  */
 package org.sonar.batch.repository.user;
 
-import com.google.common.io.InputSupplier;
+import com.google.common.io.ByteSource;
+
+import org.sonar.batch.bootstrap.WSLoader;
 import org.junit.Test;
-import org.sonar.batch.bootstrap.ServerClient;
 import org.sonar.batch.protocol.input.BatchInput;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
 import java.util.Arrays;
 
 import static org.assertj.core.api.Assertions.assertThat;
@@ -39,18 +39,17 @@ public class UserRepositoryTest {
 
   @Test
   public void testLoad() throws IOException {
-    ServerClient serverClient = mock(ServerClient.class);
-    UserRepository userRepo = new UserRepository(serverClient);
+    WSLoader wsLoader = mock(WSLoader.class);
+    UserRepository userRepo = new UserRepository(wsLoader);
 
     ByteArrayOutputStream out = new ByteArrayOutputStream();
     BatchInput.User.Builder builder = BatchInput.User.newBuilder();
     builder.setLogin("fmallet").setName("Freddy Mallet").build().writeDelimitedTo(out);
     builder.setLogin("sbrandhof").setName("Simon").build().writeDelimitedTo(out);
 
-    InputSupplier<InputStream> is = mock(InputSupplier.class);
-    when(serverClient.doRequest("/batch/users?logins=fmallet,sbrandhof", "GET", null))
-      .thenReturn(is);
-    when(is.getInput()).thenReturn(new ByteArrayInputStream(out.toByteArray()));
+    ByteSource source = mock(ByteSource.class);
+    when(wsLoader.loadSource("/batch/users?logins=fmallet,sbrandhof")).thenReturn(source);
+    when(source.openStream()).thenReturn(new ByteArrayInputStream(out.toByteArray()));
 
     assertThat(userRepo.loadFromWs(Arrays.asList("fmallet", "sbrandhof"))).extracting("login", "name").containsOnly(tuple("fmallet", "Freddy Mallet"), tuple("sbrandhof", "Simon"));
   }
diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/DefaultAnalysisModeTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/DefaultAnalysisModeTest.java
new file mode 100644 (file)
index 0000000..a2b2059
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.batch.scan;
+
+import org.sonar.batch.bootstrap.AnalysisProperties;
+import org.sonar.batch.scan.ProjectAnalysisMode;
+import org.junit.Test;
+import org.sonar.api.CoreProperties;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class DefaultAnalysisModeTest {
+
+  @Test
+  public void regular_analysis_by_default() {
+    ProjectAnalysisMode mode = new ProjectAnalysisMode(new AnalysisProperties(Collections.<String, String>emptyMap()));
+
+    assertThat(mode.isPreview()).isFalse();
+    assertThat(mode.isIncremental()).isFalse();
+
+    mode = createMode(CoreProperties.ANALYSIS_MODE, "pouet");
+
+    assertThat(mode.isPreview()).isFalse();
+    assertThat(mode.isIncremental()).isFalse();
+  }
+
+  @Test
+  public void support_analysis_mode() {
+    ProjectAnalysisMode mode = createMode(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ANALYSIS);
+
+    assertThat(mode.isPreview()).isFalse();
+    assertThat(mode.isIncremental()).isFalse();
+  }
+
+  @Test
+  public void support_preview_mode() {
+    ProjectAnalysisMode mode = createMode(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_PREVIEW);
+
+    assertThat(mode.isPreview()).isTrue();
+    assertThat(mode.isIncremental()).isFalse();
+  }
+
+  @Test
+  public void support_quick_mode() {
+    ProjectAnalysisMode mode = createMode(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_QUICK);
+
+    assertThat(mode.isPreview()).isTrue();
+    assertThat(mode.isIncremental()).isFalse();
+    assertThat(mode.isQuick()).isTrue();
+  }
+
+  @Test
+  public void support_incremental_mode() {
+    ProjectAnalysisMode mode = createMode(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_INCREMENTAL);
+
+    assertThat(mode.isPreview()).isTrue();
+    assertThat(mode.isIncremental()).isTrue();
+  }
+
+  @Test
+  public void support_deprecated_dryrun_property() {
+    ProjectAnalysisMode mode = createMode(CoreProperties.DRY_RUN, "true");
+
+    assertThat(mode.isPreview()).isTrue();
+    assertThat(mode.isIncremental()).isFalse();
+  }
+
+  private ProjectAnalysisMode createMode(String key, String value) {
+    Map<String, String> map = new HashMap<>();
+    map.put(key, value);
+
+    return new ProjectAnalysisMode(new AnalysisProperties(map));
+  }
+}
index abf12abc03405df88c9109ad9649edc08f4b5b7b..4edec303f85d288b7206010032470d1bb810c1e3 100644 (file)
@@ -27,7 +27,6 @@ import org.junit.rules.ExpectedException;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.api.config.PropertyDefinitions;
 import org.sonar.api.utils.MessageException;
-import org.sonar.batch.bootstrap.DefaultAnalysisMode;
 import org.sonar.batch.bootstrap.GlobalSettings;
 import org.sonar.batch.protocol.input.ProjectRepositories;
 
@@ -43,12 +42,12 @@ public class ModuleSettingsTest {
   public ExpectedException thrown = ExpectedException.none();
 
   ProjectRepositories projectRef;
-  private DefaultAnalysisMode mode;
+  private ProjectAnalysisMode mode;
 
   @Before
   public void before() {
     projectRef = new ProjectRepositories();
-    mode = mock(DefaultAnalysisMode.class);
+    mode = mock(ProjectAnalysisMode.class);
   }
 
   @Test
index 0970ede090873002fb58279f4ab1858c9230db9e..5b4e60f4bc0b757a326e503bf86302f56afa4aee 100644 (file)
  */
 package org.sonar.batch.scan;
 
+import org.sonar.batch.bootstrap.GlobalMode;
+
 import com.google.common.collect.ImmutableMap;
+
 import java.util.Collections;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -33,11 +37,9 @@ import org.sonar.api.utils.MessageException;
 import org.sonar.api.utils.log.LogTester;
 import org.sonar.api.utils.log.LoggerLevel;
 import org.sonar.batch.bootstrap.BootstrapProperties;
-import org.sonar.batch.bootstrap.DefaultAnalysisMode;
 import org.sonar.batch.bootstrap.GlobalSettings;
 import org.sonar.batch.protocol.input.GlobalRepositories;
 import org.sonar.batch.protocol.input.ProjectRepositories;
-
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -53,13 +55,15 @@ public class ProjectSettingsTest {
   ProjectDefinition project = ProjectDefinition.create().setKey("struts");
   GlobalSettings bootstrapProps;
 
-  private DefaultAnalysisMode mode;
+  private GlobalMode globalMode;
+  private ProjectAnalysisMode mode;
 
   @Before
   public void prepare() {
     projectRef = new ProjectRepositories();
-    mode = mock(DefaultAnalysisMode.class);
-    bootstrapProps = new GlobalSettings(new BootstrapProperties(Collections.<String, String>emptyMap()), new PropertyDefinitions(), new GlobalRepositories(), mode);
+    globalMode = mock(GlobalMode.class);
+    mode = mock(ProjectAnalysisMode.class);
+    bootstrapProps = new GlobalSettings(new BootstrapProperties(Collections.<String, String>emptyMap()), new PropertyDefinitions(), new GlobalRepositories(), globalMode);
   }
 
   @Test
@@ -118,7 +122,7 @@ public class ProjectSettingsTest {
 
   @Test
   public void should_log_a_warning_when_a_dropper_property_is_present() {
-    GlobalSettings settings = new GlobalSettings(new BootstrapProperties(ImmutableMap.of("sonar.qualitygate", "somevalue")), new PropertyDefinitions(), new GlobalRepositories(), mode);
+    GlobalSettings settings = new GlobalSettings(new BootstrapProperties(ImmutableMap.of("sonar.qualitygate", "somevalue")), new PropertyDefinitions(), new GlobalRepositories(), globalMode);
     new ProjectSettings(new ProjectReactor(project), settings, new PropertyDefinitions(), projectRef, mode);
 
     assertThat(logTester.logs(LoggerLevel.WARN)).containsOnly("Property 'sonar.qualitygate' is not supported any more. It will be ignored.");
diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/WSLoaderProjectProviderTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/WSLoaderProjectProviderTest.java
new file mode 100644 (file)
index 0000000..f3a4aa6
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.batch.scan;
+
+import org.sonar.api.batch.AnalysisMode;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.sonar.batch.bootstrap.AnalysisProperties;
+import org.sonar.batch.bootstrap.ServerClient;
+import org.sonar.batch.bootstrap.WSLoader;
+import org.sonar.batch.bootstrap.WSLoader.LoadStrategy;
+import org.sonar.home.cache.PersistentCache;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.mockito.Mockito.when;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class WSLoaderProjectProviderTest {
+  @Mock
+  private PersistentCache cache;
+
+  @Mock
+  private ServerClient client;
+
+  @Mock
+  private AnalysisMode mode;
+
+  private WSLoaderProjectProvider loaderProvider;
+  private Map<String, String> propMap;
+  private AnalysisProperties props;
+
+  @Before
+  public void setUp() {
+    MockitoAnnotations.initMocks(this);
+    loaderProvider = new WSLoaderProjectProvider();
+    propMap = new HashMap<>();
+  }
+
+  @Test
+  public void testDefault() {
+    props = new AnalysisProperties(propMap, null);
+
+    WSLoader loader = loaderProvider.provide(props, mode, cache, client);
+    assertThat(loader.getStrategy()).isEqualTo(LoadStrategy.SERVER_FIRST);
+    assertThat(loader.isCacheEnabled()).isEqualTo(false);
+  }
+
+  @Test
+  public void testSingleMode() {
+    when(mode.isQuick()).thenReturn(true);
+    when(mode.isPreview()).thenReturn(true);
+    propMap.put("sonar.enableOffline", "true");
+    props = new AnalysisProperties(propMap, null);
+
+    WSLoader loader = loaderProvider.provide(props, mode, cache, client);
+    assertThat(loader.getStrategy()).isEqualTo(LoadStrategy.CACHE_FIRST);
+    assertThat(loader.isCacheEnabled()).isEqualTo(true);
+  }
+}
index 60f85a52e173f8bcf7c4d357c4fee54968a0b204..2f5141d676e67ad51132d1808c895cfa3474fd85 100644 (file)
  */
 package org.sonar.batch.scan.filesystem;
 
+import org.sonar.batch.scan.ProjectAnalysisMode;
+
 import org.junit.Test;
 import org.mockito.Mockito;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.api.batch.fs.internal.FileMetadata;
 import org.sonar.api.config.Settings;
 import org.sonar.api.scan.filesystem.PathResolver;
-import org.sonar.batch.bootstrap.DefaultAnalysisMode;
-
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
 
@@ -37,7 +37,7 @@ public class InputFileBuilderFactoryTest {
     LanguageDetectionFactory langDetectionFactory = mock(LanguageDetectionFactory.class, Mockito.RETURNS_MOCKS);
     StatusDetectionFactory statusDetectionFactory = mock(StatusDetectionFactory.class, Mockito.RETURNS_MOCKS);
     DefaultModuleFileSystem fs = mock(DefaultModuleFileSystem.class);
-    DefaultAnalysisMode analysisMode = mock(DefaultAnalysisMode.class);
+    ProjectAnalysisMode analysisMode = mock(ProjectAnalysisMode.class);
 
     InputFileBuilderFactory factory = new InputFileBuilderFactory(ProjectDefinition.create().setKey("struts"), pathResolver, langDetectionFactory,
       statusDetectionFactory, analysisMode, new Settings(), new FileMetadata());
index ff304db2e22501080873d20d61d8e5b3b8015179..5b14b2bc9bed872fe3314b8079c08c15fc80b84a 100644 (file)
@@ -19,6 +19,8 @@
  */
 package org.sonar.batch.scan.filesystem;
 
+import org.sonar.batch.scan.ProjectAnalysisMode;
+
 import org.apache.commons.io.FileUtils;
 import org.junit.Rule;
 import org.junit.Test;
@@ -29,7 +31,6 @@ import org.sonar.api.batch.fs.internal.FileMetadata;
 import org.sonar.api.config.Settings;
 import org.sonar.api.scan.filesystem.PathResolver;
 import org.sonar.api.utils.PathUtils;
-import org.sonar.batch.bootstrap.DefaultAnalysisMode;
 
 import java.io.File;
 import java.nio.charset.StandardCharsets;
@@ -47,7 +48,7 @@ public class InputFileBuilderTest {
   LanguageDetection langDetection = mock(LanguageDetection.class);
   StatusDetection statusDetection = mock(StatusDetection.class);
   DefaultModuleFileSystem fs = mock(DefaultModuleFileSystem.class);
-  DefaultAnalysisMode analysisMode = mock(DefaultAnalysisMode.class);
+  ProjectAnalysisMode analysisMode = mock(ProjectAnalysisMode.class);
 
   @Test
   public void complete_input_file() throws Exception {
index a1bcbee460123948fbd08240f2b4fec36baf92b8..b8b74d6afabd272c334403ba98a91b47e00cd35e 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.core.util;
 
+
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Joiner;
 import com.google.common.base.Strings;
@@ -28,6 +29,7 @@ import com.google.common.io.ByteStreams;
 import com.google.common.io.CharStreams;
 import com.google.common.io.Files;
 import com.google.common.io.InputSupplier;
+
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -42,7 +44,9 @@ import java.nio.charset.StandardCharsets;
 import java.util.List;
 import java.util.Map;
 import java.util.zip.GZIPInputStream;
+
 import javax.annotation.Nullable;
+
 import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
@@ -60,13 +64,19 @@ import org.sonar.api.utils.log.Loggers;
 public class DefaultHttpDownloader extends HttpDownloader {
   private final BaseHttpDownloader downloader;
   private final Integer readTimeout;
+  private final Integer connectTimeout;
 
   public DefaultHttpDownloader(Server server, Settings settings) {
     this(server, settings, null);
   }
 
   public DefaultHttpDownloader(Server server, Settings settings, @Nullable Integer readTimeout) {
+    this(server, settings, null, readTimeout);
+  }
+
+  public DefaultHttpDownloader(Server server, Settings settings, @Nullable Integer connectTimeout, @Nullable Integer readTimeout) {
     this.readTimeout = readTimeout;
+    this.connectTimeout = connectTimeout;
     downloader = new BaseHttpDownloader(settings.getProperties(), server.getVersion());
   }
 
@@ -75,7 +85,12 @@ public class DefaultHttpDownloader extends HttpDownloader {
   }
 
   public DefaultHttpDownloader(Settings settings, @Nullable Integer readTimeout) {
+    this(settings, null, readTimeout);
+  }
+
+  public DefaultHttpDownloader(Settings settings, @Nullable Integer connectTimeout, @Nullable Integer readTimeout) {
     this.readTimeout = readTimeout;
+    this.connectTimeout = connectTimeout;
     downloader = new BaseHttpDownloader(settings.getProperties(), null);
   }
 
@@ -97,7 +112,7 @@ public class DefaultHttpDownloader extends HttpDownloader {
   @Override
   protected String readString(URI uri, Charset charset) {
     try {
-      return CharStreams.toString(CharStreams.newReaderSupplier(downloader.newInputSupplier(uri, this.readTimeout), charset));
+      return CharStreams.toString(CharStreams.newReaderSupplier(downloader.newInputSupplier(uri, this.connectTimeout, this.readTimeout), charset));
     } catch (IOException e) {
       throw failToDownload(uri, e);
     }
@@ -111,7 +126,7 @@ public class DefaultHttpDownloader extends HttpDownloader {
   @Override
   public byte[] download(URI uri) {
     try {
-      return ByteStreams.toByteArray(downloader.newInputSupplier(uri, this.readTimeout));
+      return ByteStreams.toByteArray(downloader.newInputSupplier(uri, this.connectTimeout, this.readTimeout));
     } catch (IOException e) {
       throw failToDownload(uri, e);
     }
@@ -124,7 +139,7 @@ public class DefaultHttpDownloader extends HttpDownloader {
   @Override
   public InputStream openStream(URI uri) {
     try {
-      return downloader.newInputSupplier(uri, this.readTimeout).getInput();
+      return downloader.newInputSupplier(uri, this.connectTimeout, this.readTimeout).getInput();
     } catch (IOException e) {
       throw failToDownload(uri, e);
     }
@@ -133,7 +148,7 @@ public class DefaultHttpDownloader extends HttpDownloader {
   @Override
   public void download(URI uri, File toFile) {
     try {
-      Files.copy(downloader.newInputSupplier(uri, this.readTimeout), toFile);
+      Files.copy(downloader.newInputSupplier(uri, this.connectTimeout, this.readTimeout), toFile);
     } catch (IOException e) {
       FileUtils.deleteQuietly(toFile);
       throw failToDownload(uri, e);
@@ -213,18 +228,29 @@ public class DefaultHttpDownloader extends HttpDownloader {
     }
 
     public InputSupplier<InputStream> newInputSupplier(URI uri) {
-      return new HttpInputSupplier(uri, GET, userAgent, null, null, TIMEOUT_MILLISECONDS);
+      return newInputSupplier(uri, GET, null, null, null, null);
     }
 
     public InputSupplier<InputStream> newInputSupplier(URI uri, @Nullable Integer readTimeoutMillis) {
       return newInputSupplier(uri, GET, readTimeoutMillis);
     }
 
+    /**
+     * @since 5.2
+     */
+    public InputSupplier<InputStream> newInputSupplier(URI uri, @Nullable Integer connectTimeoutMillis, @Nullable Integer readTimeoutMillis) {
+      return newInputSupplier(uri, GET, connectTimeoutMillis, readTimeoutMillis);
+    }
+
+    /**
+     * @since 5.2
+     */
+    public InputSupplier<InputStream> newInputSupplier(URI uri, String requestMethod, @Nullable Integer connectTimeoutMillis, @Nullable Integer readTimeoutMillis) {
+      return newInputSupplier(uri, requestMethod, null, null, connectTimeoutMillis, readTimeoutMillis);
+    }
+
     public InputSupplier<InputStream> newInputSupplier(URI uri, String requestMethod, @Nullable Integer readTimeoutMillis) {
-      if (readTimeoutMillis != null) {
-        return new HttpInputSupplier(uri, requestMethod, userAgent, null, null, readTimeoutMillis);
-      }
-      return new HttpInputSupplier(uri, requestMethod, userAgent, null, null, TIMEOUT_MILLISECONDS);
+      return newInputSupplier(uri, requestMethod, null, null, null, readTimeoutMillis);
     }
 
     public InputSupplier<InputStream> newInputSupplier(URI uri, String login, String password) {
@@ -235,7 +261,7 @@ public class DefaultHttpDownloader extends HttpDownloader {
      * @since 5.0
      */
     public InputSupplier<InputStream> newInputSupplier(URI uri, String requestMethod, String login, String password) {
-      return new HttpInputSupplier(uri, requestMethod, userAgent, login, password, TIMEOUT_MILLISECONDS);
+      return newInputSupplier(uri, requestMethod, login, password, null, null);
     }
 
     public InputSupplier<InputStream> newInputSupplier(URI uri, String login, String password, @Nullable Integer readTimeoutMillis) {
@@ -246,10 +272,17 @@ public class DefaultHttpDownloader extends HttpDownloader {
      * @since 5.0
      */
     public InputSupplier<InputStream> newInputSupplier(URI uri, String requestMethod, String login, String password, @Nullable Integer readTimeoutMillis) {
-      if (readTimeoutMillis != null) {
-        return new HttpInputSupplier(uri, requestMethod, userAgent, login, password, readTimeoutMillis);
-      }
-      return new HttpInputSupplier(uri, requestMethod, userAgent, login, password, TIMEOUT_MILLISECONDS);
+      return newInputSupplier(uri, requestMethod, login, password, null, readTimeoutMillis);
+    }
+
+    /**
+     * @since 5.2
+     */
+    public InputSupplier<InputStream> newInputSupplier(URI uri, String requestMethod, String login, String password, @Nullable Integer connectTimeoutMillis,
+      @Nullable Integer readTimeoutMillis) {
+      int read = readTimeoutMillis != null ? readTimeoutMillis : TIMEOUT_MILLISECONDS;
+      int connect = connectTimeoutMillis != null ? connectTimeoutMillis : TIMEOUT_MILLISECONDS;
+      return new HttpInputSupplier(uri, requestMethod, userAgent, login, password, connect, read);
     }
 
     private static class HttpInputSupplier implements InputSupplier<InputStream> {
@@ -257,22 +290,27 @@ public class DefaultHttpDownloader extends HttpDownloader {
       private final String password;
       private final URI uri;
       private final String userAgent;
+      private final int connectTimeoutMillis;
       private final int readTimeoutMillis;
       private final String requestMethod;
 
-      HttpInputSupplier(URI uri, String requestMethod, String userAgent, String login, String password, int readTimeoutMillis) {
+      HttpInputSupplier(URI uri, String requestMethod, String userAgent, String login, String password, int connectTimeoutMillis, int readTimeoutMillis) {
         this.uri = uri;
         this.requestMethod = requestMethod;
         this.userAgent = userAgent;
         this.login = login;
         this.password = password;
         this.readTimeoutMillis = readTimeoutMillis;
+        this.connectTimeoutMillis = connectTimeoutMillis;
       }
 
+      /**
+       * @throws IOException any I/O error, not limited to the network connection
+       * @throws HttpException if HTTP response code > 400
+       */
       @Override
       public InputStream getInput() throws IOException {
         Loggers.get(getClass()).debug("Download: " + uri + " (" + getProxySynthesis(uri, ProxySelector.getDefault()) + ")");
-
         HttpURLConnection connection = (HttpURLConnection) uri.toURL().openConnection();
         connection.setRequestMethod(requestMethod);
         HttpsTrust.INSTANCE.trust(connection);
@@ -283,7 +321,7 @@ public class DefaultHttpDownloader extends HttpDownloader {
           String encoded = Base64.encodeBase64String((login + ":" + password).getBytes(StandardCharsets.UTF_8));
           connection.setRequestProperty("Authorization", "Basic " + encoded);
         }
-        connection.setConnectTimeout(TIMEOUT_MILLISECONDS);
+        connection.setConnectTimeout(connectTimeoutMillis);
         connection.setReadTimeout(readTimeoutMillis);
         connection.setUseCaches(true);
         connection.setInstanceFollowRedirects(true);
index 8ca54546371ab94e6307196f84a82f994be82a14..f19f42c5345711c8acebd1c295542bdbda47ab08 100644 (file)
@@ -33,6 +33,7 @@ import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.Properties;
 import java.util.zip.GZIPOutputStream;
+
 import org.hamcrest.BaseMatcher;
 import org.hamcrest.Description;
 import org.junit.AfterClass;
@@ -51,7 +52,6 @@ import org.simpleframework.transport.connect.SocketConnection;
 import org.sonar.api.config.Settings;
 import org.sonar.api.platform.Server;
 import org.sonar.api.utils.SonarException;
-
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.mock;
@@ -123,6 +123,25 @@ public class DefaultHttpDownloaderTest {
     }
   }
 
+  @Test(timeout = 10000)
+  public void readStringConnectTimeout() throws IOException, URISyntaxException {
+    // non routable address
+    String url = "http://10.255.255.1";
+
+    thrown.expect(new BaseMatcher<Exception>() {
+      @Override
+      public boolean matches(Object ex) {
+        return ex instanceof SonarException && ((SonarException) ex).getCause() instanceof SocketTimeoutException;
+      }
+
+      @Override
+      public void describeTo(Description arg0) {
+      }
+    });
+    DefaultHttpDownloader downloader = new DefaultHttpDownloader(new Settings(), 10, 50000);
+    downloader.openStream(new URI(url));
+  }
+
   @Test
   public void downloadBytes() throws URISyntaxException {
     byte[] bytes = new DefaultHttpDownloader(new Settings()).readBytes(new URI(baseUrl));
index 95b93e6ed2701f22af0f3f5556c1f92f15046bae..e4c8bd0291207b34b6af7cd1f1e597e24c22e906 100644 (file)
@@ -32,7 +32,6 @@ import java.nio.file.Path;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
-import java.util.concurrent.Callable;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -51,25 +50,18 @@ public class PersistentCache {
 
   // eviction strategy is to expire entries after modification once a time duration has elapsed
   private final long defaultDurationToExpireMs;
-  private boolean forceUpdate;
   private final Logger logger;
 
-  public PersistentCache(Path baseDir, long defaultDurationToExpireMs, boolean forceUpdate, Logger logger) {
+  public PersistentCache(Path baseDir, long defaultDurationToExpireMs, Logger logger) {
     this.baseDir = baseDir;
     this.defaultDurationToExpireMs = defaultDurationToExpireMs;
     this.logger = logger;
 
-    reconfigure(forceUpdate);
+    reconfigure();
     logger.debug("cache: " + baseDir + ", default expiration time (ms): " + defaultDurationToExpireMs);
   }
 
-  public void reconfigure(boolean forceUpdate) {
-    this.forceUpdate = forceUpdate;
-
-    if (forceUpdate) {
-      logger.debug("cache: forcing update");
-    }
-
+  public void reconfigure() {
     try {
       Files.createDirectories(baseDir);
     } catch (IOException e) {
@@ -81,16 +73,12 @@ public class PersistentCache {
     return baseDir;
   }
 
-  public boolean isForceUpdate() {
-    return forceUpdate;
-  }
-
   @CheckForNull
-  public synchronized String getString(@Nonnull String obj, @Nullable final Callable<String> valueLoader) throws Exception {
-    byte[] cached = get(obj, new Callable<byte[]>() {
+  public synchronized String getString(@Nonnull String obj, @Nullable final PersistentCacheLoader<String> valueLoader) throws IOException {
+    byte[] cached = get(obj, new PersistentCacheLoader<byte[]>() {
       @Override
-      public byte[] call() throws Exception {
-        String s = valueLoader.call();
+      public byte[] get() throws IOException {
+        String s = valueLoader.get();
         if (s != null) {
           return s.getBytes(ENCODING);
         }
@@ -106,26 +94,23 @@ public class PersistentCache {
   }
 
   @CheckForNull
-  public synchronized byte[] get(@Nonnull String obj, @Nullable Callable<byte[]> valueLoader) throws Exception {
+  public synchronized byte[] get(@Nonnull String obj, @Nullable PersistentCacheLoader<byte[]> valueLoader) throws IOException {
     String key = getKey(obj);
 
     try {
       lock();
-      if (!forceUpdate) {
-        byte[] cached = getCache(key);
 
-        if (cached != null) {
-          logger.debug("cache hit for " + obj + " -> " + key);
-          return cached;
-        }
+      byte[] cached = getCache(key);
 
-        logger.debug("cache miss for " + obj + " -> " + key);
-      } else {
-        logger.debug("cache force update for " + obj + " -> " + key);
+      if (cached != null) {
+        logger.debug("cache hit for " + obj + " -> " + key);
+        return cached;
       }
 
+      logger.debug("cache miss for " + obj + " -> " + key);
+
       if (valueLoader != null) {
-        byte[] value = valueLoader.call();
+        byte[] value = valueLoader.get();
         if (value != null) {
           putCache(key, value);
         }
@@ -138,6 +123,16 @@ public class PersistentCache {
     return null;
   }
 
+  public synchronized void put(@Nonnull String obj, @Nonnull byte[] value) throws IOException {
+    String key = getKey(obj);
+    try {
+      lock();
+      putCache(key, value);
+    } finally {
+      unlock();
+    }
+  }
+
   /**
    * Deletes all cache entries
    */
index e115f1cab6416191499a6ad9563ff2991ceabf7c..f0fa159691737740f2a48ad74bfc464e70bb9d35 100644 (file)
@@ -28,7 +28,6 @@ public class PersistentCacheBuilder {
   private static final long DEFAULT_EXPIRE_DURATION = TimeUnit.MILLISECONDS.convert(1L, TimeUnit.DAYS);
   private static final String DIR_NAME = "ws_cache";
 
-  private boolean forceUpdate = false;
   private Path cachePath;
   private final Logger logger;
 
@@ -41,7 +40,7 @@ public class PersistentCacheBuilder {
       setSonarHome(findHome());
     }
 
-    return new PersistentCache(cachePath, DEFAULT_EXPIRE_DURATION, forceUpdate, logger);
+    return new PersistentCache(cachePath, DEFAULT_EXPIRE_DURATION, logger);
   }
 
   public PersistentCacheBuilder setSonarHome(@Nullable Path p) {
@@ -51,11 +50,6 @@ public class PersistentCacheBuilder {
     return this;
   }
 
-  public PersistentCacheBuilder forceUpdate(boolean update) {
-    this.forceUpdate = update;
-    return this;
-  }
-
   private static Path findHome() {
     String home = System.getenv("SONAR_USER_HOME");
 
diff --git a/sonar-home/src/main/java/org/sonar/home/cache/PersistentCacheLoader.java b/sonar-home/src/main/java/org/sonar/home/cache/PersistentCacheLoader.java
new file mode 100644 (file)
index 0000000..ee906b4
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.home.cache;
+
+import java.io.IOException;
+
+public interface PersistentCacheLoader<T> {
+  T get() throws IOException;
+}
index 09335dffc3b6ba6d07aec572b572a1f9e7ea3386..62e105bf861a9f637ffd3ac951f778477e94902e 100644 (file)
@@ -20,7 +20,6 @@
 package org.sonar.home.cache;
 
 import java.io.File;
-import java.util.concurrent.Callable;
 import org.apache.commons.io.FileUtils;
 import org.junit.Before;
 import org.junit.Rule;
@@ -42,7 +41,7 @@ public class PersistentCacheTest {
 
   @Before
   public void setUp() {
-    cache = new PersistentCache(tmp.getRoot().toPath(), Long.MAX_VALUE, false, mock(Logger.class));
+    cache = new PersistentCache(tmp.getRoot().toPath(), Long.MAX_VALUE, mock(Logger.class));
   }
 
   @Test
@@ -59,9 +58,9 @@ public class PersistentCacheTest {
   @Test
   public void testNullValue() throws Exception {
     // mocks have their methods returning null by default
-    Callable<byte[]> c = mock(Callable.class);
+    PersistentCacheLoader<byte[]> c = mock(PersistentCacheLoader.class);
     assertThat(cache.get(URI, c)).isNull();
-    verify(c).call();
+    verify(c).get();
     assertCacheHit(false);
   }
 
@@ -78,30 +77,17 @@ public class PersistentCacheTest {
     assertCacheHit(true);
   }
 
-  @Test
-  public void testForceUpdate() throws Exception {
-    cache = new PersistentCache(tmp.getRoot().toPath(), Long.MAX_VALUE, true, mock(Logger.class));
-
-    assertCacheHit(false);
-    assertCacheHit(false);
-    assertCacheHit(false);
-
-    // with forceUpdate, it should still have cached the last call
-    cache = new PersistentCache(tmp.getRoot().toPath(), Long.MAX_VALUE, false, mock(Logger.class));
-    assertCacheHit(true);
-  }
-
   @Test
   public void testReconfigure() throws Exception {
-    cache = new PersistentCache(tmp.getRoot().toPath(), Long.MAX_VALUE, true, mock(Logger.class));
-    assertCacheHit(false);
+    cache = new PersistentCache(tmp.getRoot().toPath(), Long.MAX_VALUE, mock(Logger.class));
     assertCacheHit(false);
+    assertCacheHit(true);
 
     File root = tmp.getRoot();
     FileUtils.deleteQuietly(root);
 
     // should re-create cache directory and start using the cache
-    cache.reconfigure(false);
+    cache.reconfigure();
     assertThat(root).exists();
 
     assertCacheHit(false);
@@ -111,7 +97,7 @@ public class PersistentCacheTest {
   @Test
   public void testExpiration() throws Exception {
     // negative time to make sure it is expired on the second call
-    cache = new PersistentCache(tmp.getRoot().toPath(), -100, false, mock(Logger.class));
+    cache = new PersistentCache(tmp.getRoot().toPath(), -100, mock(Logger.class));
     assertCacheHit(false);
     assertCacheHit(false);
   }
@@ -122,11 +108,11 @@ public class PersistentCacheTest {
     assertThat(c.wasCalled).isEqualTo(!hit);
   }
 
-  private class CacheFillerString implements Callable<String> {
+  private class CacheFillerString implements PersistentCacheLoader<String> {
     public boolean wasCalled = false;
 
     @Override
-    public String call() throws Exception {
+    public String get() {
       wasCalled = true;
       return VALUE;
     }
@@ -139,8 +125,8 @@ public class PersistentCacheTest {
    */
   @Test(expected = ArithmeticException.class)
   public void testExceptions() throws Exception {
-    Callable<byte[]> c = mock(Callable.class);
-    when(c.call()).thenThrow(ArithmeticException.class);
+    PersistentCacheLoader<byte[]> c = mock(PersistentCacheLoader.class);
+    when(c.get()).thenThrow(ArithmeticException.class);
     cache.get(URI, c);
   }
 
index 13c47e7ce0d4d9c0ab590886133092bb040fede9..247b9097e5ede2f81cb206aaee49672d87f5a5af 100644 (file)
@@ -446,6 +446,11 @@ public interface CoreProperties {
    * @since 4.0
    */
   String ANALYSIS_MODE_PREVIEW = "preview";
+  
+  /**
+   * @since 5.2
+   */
+  String ANALYSIS_MODE_QUICK = "quick";
 
   /**
    * @since 4.0
index 2fad14d30c30adf3ca0bf21a6b4eb616a958b2ac..37734df0917b2424a50ddef84b96fe07b1989919 100644 (file)
@@ -29,5 +29,7 @@ public interface AnalysisMode {
   boolean isPreview();
 
   boolean isIncremental();
+  
+  boolean isQuick();
 
 }
index 9d574bb86bd222450f399fbee9757c0f23766a1f..8bec2b65ea4aab3582808a5ef9f7ea5fdd703619 100644 (file)
@@ -230,6 +230,7 @@ public class SensorContextTester implements SensorContext {
   public static class MockAnalysisMode implements AnalysisMode {
     private boolean isIncremental = false;
     private boolean isPreview = false;
+    private boolean isSingle = false;
 
     @Override
     public boolean isIncremental() {
@@ -248,6 +249,15 @@ public class SensorContextTester implements SensorContext {
     public void setPreview(boolean value) {
       this.isPreview = value;
     }
+
+    @Override
+    public boolean isQuick() {
+      return this.isSingle;
+    }
+    
+    public void setSingle(boolean single) {
+      this.isSingle = single;
+    }
   }
 
   private static class InMemorySensorStorage implements SensorStorage {