aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-batch
diff options
context:
space:
mode:
authorDuarte Meneses <duarte.meneses@sonarsource.com>2015-05-29 11:29:09 +0200
committerDuarte Meneses <duarte.meneses@sonarsource.com>2015-06-04 10:53:02 +0200
commit8f1df5c561f9bd1a94aaf95dcda0b474bab472d2 (patch)
tree053ca3cbe8f041eca854c0314bbeb0229034790e /sonar-batch
parent35f206a6cefc849aa105769c2545d30257188de4 (diff)
downloadsonarqube-8f1df5c561f9bd1a94aaf95dcda0b474bab472d2.tar.gz
sonarqube-8f1df5c561f9bd1a94aaf95dcda0b474bab472d2.zip
SONAR-6577 Offline mode in preview mode
Diffstat (limited to 'sonar-batch')
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java1
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/bootstrap/PersistentCacheProvider.java51
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java59
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/repository/DefaultServerIssuesLoader.java15
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/repository/user/UserRepository.java29
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/bootstrap/PersistentCacheProviderTest.java62
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/bootstrap/ServerClientTest.java68
7 files changed, 255 insertions, 30 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java
index f24fe52fbe8..facf6c58a3a 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java
@@ -112,6 +112,7 @@ public class GlobalContainer extends ComponentContainer {
DefaultHttpDownloader.class,
UriReader.class,
new FileCacheProvider(),
+ new PersistentCacheProvider(),
System2.INSTANCE,
DefaultI18n.class,
Durations.class,
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/PersistentCacheProvider.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/PersistentCacheProvider.java
new file mode 100644
index 00000000000..eca9407bce9
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/PersistentCacheProvider.java
@@ -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.sonar.home.cache.PersistentCacheBuilder;
+import org.picocontainer.injectors.ProviderAdapter;
+
+import java.nio.file.Paths;
+
+import org.sonar.home.cache.PersistentCache;
+
+public class PersistentCacheProvider extends ProviderAdapter {
+ private PersistentCache cache;
+
+ public PersistentCache provide(BootstrapProperties props) {
+ if (cache == null) {
+ PersistentCacheBuilder builder = new PersistentCacheBuilder();
+
+ String forceUpdate = props.property("sonar.forceUpdate");
+
+ if ("true".equals(forceUpdate)) {
+ builder.forceUpdate(true);
+ }
+
+ String home = props.property("sonar.userHome");
+ if (home != null) {
+ builder.setSonarHome(Paths.get(home));
+ }
+
+ cache = builder.build();
+ }
+ return cache;
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java
index db2d261b1bb..c0574deddd6 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java
@@ -19,6 +19,7 @@
*/
package org.sonar.batch.bootstrap;
+import org.sonar.home.cache.PersistentCache;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
@@ -39,6 +40,7 @@ import org.sonar.core.util.DefaultHttpDownloader;
import javax.annotation.Nullable;
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -47,6 +49,7 @@ import java.net.URI;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Callable;
/**
* Replace the deprecated org.sonar.batch.ServerMetadata
@@ -59,11 +62,15 @@ 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) {
+ public ServerClient(BootstrapProperties settings, EnvironmentInformation env, PersistentCache cache, DefaultAnalysisMode mode) {
this.props = settings;
this.downloader = new DefaultHttpDownloader.BaseHttpDownloader(settings.properties(), env.toString());
+ this.cache = cache;
+ this.mode = mode;
}
public String getURL() {
@@ -102,31 +109,63 @@ public class ServerClient {
}
public String request(String pathStartingWithSlash, String requestMethod, boolean wrapHttpException, @Nullable Integer timeoutMillis) {
- InputSupplier<InputStream> inputSupplier = doRequest(pathStartingWithSlash, requestMethod, timeoutMillis);
+ final byte[] buf = load(pathStartingWithSlash, requestMethod, wrapHttpException, timeoutMillis);
try {
- return IOUtils.toString(inputSupplier.getInput(), "UTF-8");
- } catch (HttpDownloader.HttpException e) {
- throw wrapHttpException ? handleHttpException(e) : e;
- } catch (IOException e) {
+ return new String(buf, "UTF-8");
+ } catch (UnsupportedEncodingException 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);
+
try {
+ if (GET.equals(requestMethod) && mode.isPreview()) {
+ return cache.get(uri.toString(), new HttpValueLoader(uri, requestMethod, timeoutMillis));
+ } else {
+ return new HttpValueLoader(uri, requestMethod, timeoutMillis).call();
+ }
+ } 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);
} else {
inputSupplier = downloader.newInputSupplier(uri, requestMethod, getLogin(), getPassword(), timeoutMillis);
}
- return inputSupplier;
- } catch (Exception e) {
- throw new IllegalStateException(String.format("Unable to request: %s", uri), e);
+ return IOUtils.toByteArray(inputSupplier.getInput());
}
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultServerIssuesLoader.java b/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultServerIssuesLoader.java
index 8193d3a3ebe..e71e9eb54cb 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultServerIssuesLoader.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultServerIssuesLoader.java
@@ -38,18 +38,23 @@ public class DefaultServerIssuesLoader implements ServerIssuesLoader {
@Override
public void load(String componentKey, Function<ServerIssue, Void> consumer, boolean incremental) {
- InputSupplier<InputStream> request = serverClient.doRequest("/batch/issues?key=" + ServerClient.encodeForUrl(componentKey), "GET", null);
- try (InputStream is = request.getInput()) {
+ 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);
+ }
+ }
+
+ private static void parseIssues(InputSupplier<InputStream> input, Function<ServerIssue, Void> consumer) {
+ try (InputStream is = input.getInput()) {
ServerIssue previousIssue = ServerIssue.parseDelimitedFrom(is);
while (previousIssue != null) {
consumer.apply(previousIssue);
previousIssue = ServerIssue.parseDelimitedFrom(is);
}
- } catch (HttpDownloader.HttpException e) {
- throw serverClient.handleHttpException(e);
} catch (IOException e) {
throw new IllegalStateException("Unable to get previous issues", e);
}
}
-
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/user/UserRepository.java b/sonar-batch/src/main/java/org/sonar/batch/repository/user/UserRepository.java
index 099b9470903..7208086c308 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/repository/user/UserRepository.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/repository/user/UserRepository.java
@@ -46,24 +46,35 @@ public class UserRepository {
if (userLogins.isEmpty()) {
return Collections.emptyList();
}
- 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);
+
+ 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);
+
+ } catch (HttpDownloader.HttpException e) {
+ throw serverClient.handleHttpException(e);
+ }
+ }
+
+ private static Collection<BatchInput.User> parseUsers(InputSupplier<InputStream> input) {
List<BatchInput.User> users = new ArrayList<>();
- try (InputStream is = request.getInput()) {
+
+ try (InputStream is = input.getInput()) {
BatchInput.User user = BatchInput.User.parseDelimitedFrom(is);
while (user != null) {
users.add(user);
user = BatchInput.User.parseDelimitedFrom(is);
}
- } catch (HttpDownloader.HttpException e) {
- throw serverClient.handleHttpException(e);
} catch (IOException e) {
throw new IllegalStateException("Unable to get user details from server", e);
}
+
return users;
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/PersistentCacheProviderTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/PersistentCacheProviderTest.java
new file mode 100644
index 00000000000..fb07230e741
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/PersistentCacheProviderTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.junit.Before;
+
+import static org.mockito.Mockito.when;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import org.junit.Test;
+
+public class PersistentCacheProviderTest {
+ private PersistentCacheProvider provider = null;
+
+ @Mock
+ private BootstrapProperties props = null;
+
+ @Before
+ public void prepare() {
+ MockitoAnnotations.initMocks(this);
+ provider = new PersistentCacheProvider();
+ }
+
+ @Test
+ public void test_singleton() {
+ assertThat(provider.provide(props)).isEqualTo(provider.provide(props));
+ }
+
+ @Test
+ public void test_cache_dir() {
+ assertThat(provider.provide(props).getBaseDirectory().toFile()).exists().isDirectory();
+ }
+
+ @Test
+ public void test_forceUpdate() {
+ // normally don't force update
+ assertThat(provider.provide(props).isForceUpdate()).isFalse();
+
+ when(props.property("sonar.forceUpdate")).thenReturn("true");
+ provider = new PersistentCacheProvider();
+ assertThat(provider.provide(props).isForceUpdate()).isTrue();
+ }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ServerClientTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ServerClientTest.java
index b50d3643887..beab35ba672 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ServerClientTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ServerClientTest.java
@@ -19,7 +19,9 @@
*/
package org.sonar.batch.bootstrap;
-import com.google.common.io.Files;
+import org.junit.Before;
+import org.sonar.home.cache.PersistentCacheBuilder;
+import org.sonar.home.cache.PersistentCache;
import org.apache.commons.io.IOUtils;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
@@ -39,6 +41,7 @@ import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
import static javax.servlet.http.HttpServletResponse.SC_OK;
import static org.apache.commons.io.IOUtils.write;
@@ -52,23 +55,71 @@ public class ServerClientTest {
@Rule
public TemporaryFolder temp = new TemporaryFolder();
@Rule
+ public TemporaryFolder cacheTmp = new TemporaryFolder();
+ @Rule
public ExpectedException thrown = ExpectedException.none();
- MockHttpServer server = null;
- BootstrapProperties bootstrapProps = mock(BootstrapProperties.class);
+ 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() {
if (server != null) {
server.stop();
}
}
+
+ @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/");
- ServerClient client = new ServerClient(settings, new EnvironmentInformation("Junit", "4"));
+ PersistentCache ps = new PersistentCacheBuilder().setSonarHome(cacheTmp.getRoot().toPath()).build();
+ ServerClient client = new ServerClient(settings, new EnvironmentInformation("Junit", "4"), ps, mode);
assertThat(client.getURL()).isEqualTo("http://localhost:8080/sonar");
}
@@ -99,7 +150,7 @@ public class ServerClientTest {
File file = temp.newFile();
newServerClient().download("/foo", file);
- assertThat(Files.toString(file, StandardCharsets.UTF_8)).isEqualTo("this is the content");
+ assertThat(new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8)).isEqualTo("this is the content");
}
@Test
@@ -153,7 +204,12 @@ public class ServerClientTest {
private ServerClient newServerClient() {
when(bootstrapProps.property("sonar.host.url")).thenReturn("http://localhost:" + server.getPort());
- return new ServerClient(bootstrapProps, new EnvironmentInformation("Junit", "4"));
+ PersistentCache ps = new PersistentCacheBuilder().setSonarHome(cacheTmp.getRoot().toPath()).build();
+ return new ServerClient(bootstrapProps, new EnvironmentInformation("Junit", "4"), ps, mode);
+ }
+
+ private int getNumFilesInCache() {
+ return new File(cacheTmp.getRoot(), "ws_cache").listFiles().length;
}
static class MockHttpServer {