]> source.dussan.org Git - sonar-scanner-cli.git/commitdiff
SONARUNNER-132 Add support for offline mode
authorDuarte Meneses <duarte.meneses@sonarsource.com>
Mon, 20 Jul 2015 11:30:07 +0000 (13:30 +0200)
committerDuarte Meneses <duarte.meneses@sonarsource.com>
Fri, 24 Jul 2015 12:32:25 +0000 (14:32 +0200)
it/src/test/java/com/sonar/runner/it/MultimoduleTest.java
it/src/test/java/com/sonar/runner/it/SonarRunnerTestSuite.java
sonar-runner-api/src/main/java/org/sonar/runner/impl/IsolatedLauncherFactory.java
sonar-runner-api/src/main/java/org/sonar/runner/impl/ServerConnection.java
sonar-runner-api/src/test/java/org/sonar/runner/impl/ServerConnectionTest.java

index 35dc8d9c60a577ae107a9d8621513eb2cf0c1769..6f2a25ca83993ff6f3a8549047cc5c0c31ff0ec6 100644 (file)
@@ -28,7 +28,6 @@ import org.sonar.wsclient.services.ResourceQuery;
 import java.io.File;
 
 import static org.fest.assertions.Assertions.assertThat;
-import static org.junit.Assume.assumeTrue;
 
 public class MultimoduleTest extends RunnerTestCase {
 
index e33768d5a6f81dddade97a4c778bb8a3d0972729..b2193662368ae1897c1f4fcfaf74f3e6d5808c2c 100644 (file)
@@ -24,7 +24,7 @@ import org.junit.runners.Suite;
 import org.junit.runners.Suite.SuiteClasses;
 
 @RunWith(Suite.class)
-@SuiteClasses({JavaTest.class, MultimoduleTest.class})
+@SuiteClasses({JavaTest.class, MultimoduleTest.class, CacheTest.class})
 public class SonarRunnerTestSuite {
 
 }
index 5c2a590ee1b1b0f6651e41474cf1f590cd290f68..c56b72d9dc5bee81c39246e46182aad6a59f8e97 100644 (file)
@@ -50,11 +50,6 @@ public class IsolatedLauncherFactory {
 
   private PersistentCache getCache(Properties props) {
     PersistentCacheBuilder builder = new PersistentCacheBuilder(logger);
-
-    if (!"true".equals(props.getProperty("sonar.enableHttpCache"))) {
-      builder.forceUpdate(true);
-    }
-
     return builder.build();
   }
 
index 7abd093944553ff08c02c54489f1863887319b88..da9a86ceedfa4efb16ec99e0361c4657b5157145 100644 (file)
  */
 package org.sonar.runner.impl;
 
+import com.github.kevinsawicki.http.HttpRequest.HttpRequestException;
+
 import com.github.kevinsawicki.http.HttpRequest;
+
 import java.io.File;
 import java.io.IOException;
 import java.net.ConnectException;
@@ -27,9 +30,9 @@ import java.net.URL;
 import java.net.UnknownHostException;
 import java.text.MessageFormat;
 import java.util.Properties;
-import java.util.concurrent.Callable;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+
 import org.apache.commons.io.FileUtils;
 import org.sonar.home.cache.Logger;
 import org.sonar.home.cache.PersistentCache;
@@ -38,7 +41,7 @@ class ServerConnection {
 
   private static final String SONAR_SERVER_CAN_NOT_BE_REACHED = "Sonar server ''{0}'' can not be reached";
   private static final String STATUS_RETURNED_BY_URL_IS_INVALID = "Status returned by url : ''{0}'' is invalid : {1}";
-  static final int CONNECT_TIMEOUT_MILLISECONDS = 30000;
+  static final int CONNECT_TIMEOUT_MILLISECONDS = 5000;
   static final int READ_TIMEOUT_MILLISECONDS = 60000;
   private static final Pattern CHARSET_PATTERN = Pattern.compile("(?i)\\bcharset=\\s*\"?([^\\s;\"]*)");
 
@@ -46,15 +49,15 @@ class ServerConnection {
   private final String userAgent;
 
   private final PersistentCache wsCache;
-  private final boolean isModePreview;
+  private final boolean isCacheEnable;
   private final Logger logger;
 
-  private ServerConnection(String serverUrl, String app, String appVersion, boolean preview, PersistentCache cache, Logger logger) {
+  private ServerConnection(String serverUrl, String app, String appVersion, boolean isCacheEnable, PersistentCache cache, Logger logger) {
     this.logger = logger;
     this.serverUrl = removeEndSlash(serverUrl);
     this.userAgent = app + "/" + appVersion;
     this.wsCache = cache;
-    this.isModePreview = preview;
+    this.isCacheEnable = isCacheEnable;
   }
 
   private static String removeEndSlash(String url) {
@@ -69,36 +72,42 @@ class ServerConnection {
     String app = properties.getProperty(InternalProperties.RUNNER_APP);
     String appVersion = properties.getProperty(InternalProperties.RUNNER_APP_VERSION);
     String analysisMode = properties.getProperty("sonar.analysis.mode");
-    boolean preview = "preview".equalsIgnoreCase(analysisMode);
+    String enableOffline = properties.getProperty("sonar.enableOffline");
+    boolean enableCache = "preview".equalsIgnoreCase(analysisMode) && "true".equals(enableOffline);
 
-    return new ServerConnection(serverUrl, app, appVersion, preview, cache, logger);
+    return new ServerConnection(serverUrl, app, appVersion, enableCache, cache, logger);
   }
 
-  private class StringDownloader implements Callable<String> {
-    private String url;
-
-    StringDownloader(String url) {
-      this.url = url;
-    }
+  /**
+   * 
+   * @throws HttpRequestException If there is an underlying IOException related to the connection
+   * @throws IOException If the HTTP response code is != 200
+   */
+  private String downloadString(String url, boolean saveCache) throws HttpRequestException, IOException {
+    HttpRequest httpRequest = null;
+    try {
+      httpRequest = newHttpRequest(new URL(url));
+      String charset = getCharsetFromContentType(httpRequest.contentType());
+      if (charset == null || "".equals(charset)) {
+        charset = "UTF-8";
+      }
+      if (!httpRequest.ok()) {
+        throw new IOException(MessageFormat.format(STATUS_RETURNED_BY_URL_IS_INVALID, url, httpRequest.code()));
+      }
 
-    @Override
-    public String call() throws Exception {
-      HttpRequest httpRequest = null;
-      try {
-        httpRequest = newHttpRequest(new URL(url));
-        String charset = getCharsetFromContentType(httpRequest.contentType());
-        if (charset == null || "".equals(charset)) {
-          charset = "UTF-8";
-        }
-        if (!httpRequest.ok()) {
-          throw new IOException(MessageFormat.format(STATUS_RETURNED_BY_URL_IS_INVALID, url, httpRequest.code()));
-        }
-        return httpRequest.body(charset);
-      } finally {
-        if (httpRequest != null) {
-          httpRequest.disconnect();
+      byte[] body = httpRequest.bytes();
+      if (saveCache) {
+        try {
+          wsCache.put(url, body);
+        } catch (IOException e) {
+          logger.warn("Failed to cache WS call: " + e.getMessage());
         }
       }
+      return new String(body, charset);
+    } finally {
+      if (httpRequest != null) {
+        httpRequest.disconnect();
+      }
     }
   }
 
@@ -121,22 +130,42 @@ class ServerConnection {
     }
   }
 
-  String downloadStringCache(String path) throws Exception {
+  /**
+   * Tries to fetch from server and falls back to cache. If both attempts fail, it throws the exception 
+   * linked to the server connection failure.
+   */
+  String downloadStringCache(String path) throws IOException {
     String fullUrl = serverUrl + path;
     try {
-      if (isModePreview) {
-        return wsCache.getString(serverUrl, new StringDownloader(fullUrl));
-      } else {
-        return new StringDownloader(fullUrl).call();
-      }
+      return downloadString(fullUrl, isCacheEnable);
     } catch (HttpRequest.HttpRequestException e) {
       if (e.getCause() instanceof ConnectException || e.getCause() instanceof UnknownHostException) {
-        logger.error(MessageFormat.format(SONAR_SERVER_CAN_NOT_BE_REACHED, serverUrl));
+        if (isCacheEnable) {
+          return fallbackToCache(fullUrl, e);
+        }
       }
+
+      logger.error(MessageFormat.format(SONAR_SERVER_CAN_NOT_BE_REACHED, serverUrl));
       throw e;
     }
   }
 
+  private String fallbackToCache(String fullUrl, HttpRequest.HttpRequestException originalException) {
+    logger.info(MessageFormat.format(SONAR_SERVER_CAN_NOT_BE_REACHED + ", trying cache", serverUrl));
+
+    try {
+      String cached = wsCache.getString(fullUrl, null);
+      if (cached != null) {
+        return cached;
+      }
+      logger.error(MessageFormat.format(SONAR_SERVER_CAN_NOT_BE_REACHED + " and had a cache miss", serverUrl));
+      throw originalException;
+    } catch (IOException e) {
+      throw new IllegalStateException("Failed to access cache", e);
+    }
+
+  }
+
   private HttpRequest newHttpRequest(URL url) {
     HttpRequest request = HttpRequest.get(url);
     request.trustAllCerts().trustAllHosts();
index 20016d849ff9e6099d4cb42ccdcc298c6050a8b3..7e9dec4702afb9c9b81cff71bec36540ec11e592 100644 (file)
  */
 package org.sonar.runner.impl;
 
+import com.github.kevinsawicki.http.HttpRequest;
+
 import java.io.File;
+import java.io.IOException;
+import java.net.ConnectException;
 import java.util.Properties;
+
 import org.apache.commons.io.FileUtils;
 import org.junit.Before;
 import org.junit.Rule;
@@ -29,7 +34,6 @@ import org.junit.rules.TemporaryFolder;
 import org.sonar.home.cache.Logger;
 import org.sonar.home.cache.PersistentCache;
 import org.sonar.home.cache.PersistentCacheBuilder;
-
 import static org.fest.assertions.Assertions.assertThat;
 import static org.fest.assertions.Fail.fail;
 import static org.mockito.Mockito.mock;
@@ -81,6 +85,7 @@ public class ServerConnectionTest {
     Properties props = new Properties();
     props.setProperty("sonar.host.url", httpServer.url() + "/");
     props.setProperty("sonar.analysis.mode", "preview");
+    props.setProperty("sonar.enableOffline", "true");
 
     assertThat(cacheDir.list().length).isEqualTo(0);
     ServerConnection connection = ServerConnection.create(props, cache, mock(Logger.class));
@@ -89,10 +94,37 @@ public class ServerConnectionTest {
     assertThat(str).isEqualTo("abcde");
     assertThat(cacheDir.list().length).isEqualTo(2);
 
-    httpServer.setMockResponseData("never requested");
+    httpServer.after();
     str = connection.downloadStringCache("/batch/index.txt");
     assertThat(str).isEqualTo("abcde");
   }
+  
+  @Test
+  public void should_throw_connection_exception_() throws IOException {
+    File cacheDir = new File(temp.getRoot(), "ws_cache");
+    httpServer.setMockResponseData("abcde");
+    Properties props = new Properties();
+    props.setProperty("sonar.host.url", httpServer.url() + "/");
+
+    assertThat(cacheDir.list().length).isEqualTo(0);
+    ServerConnection connection = ServerConnection.create(props, cache, mock(Logger.class));
+    String str = connection.downloadStringCache("/batch/index.txt");
+    assertThat(str).isEqualTo("abcde");
+    
+    httpServer.after();
+    
+    try {
+      connection.downloadStringCache("/batch/index.txt");
+      fail("exception expected");
+    } catch(HttpRequest.HttpRequestException e) {
+      //expected
+      assertThat(e.getCause()).isInstanceOf(ConnectException.class);
+      
+      //cache never used
+      assertThat(cacheDir.list().length).isEqualTo(0);
+    }
+    
+  }
 
   @Test
   public void should_not_cache_not_preview() throws Exception {