diff options
author | Duarte Meneses <duarte.meneses@sonarsource.com> | 2015-05-29 11:29:09 +0200 |
---|---|---|
committer | Duarte Meneses <duarte.meneses@sonarsource.com> | 2015-06-04 10:53:02 +0200 |
commit | 8f1df5c561f9bd1a94aaf95dcda0b474bab472d2 (patch) | |
tree | 053ca3cbe8f041eca854c0314bbeb0229034790e /sonar-batch | |
parent | 35f206a6cefc849aa105769c2545d30257188de4 (diff) | |
download | sonarqube-8f1df5c561f9bd1a94aaf95dcda0b474bab472d2.tar.gz sonarqube-8f1df5c561f9bd1a94aaf95dcda0b474bab472d2.zip |
SONAR-6577 Offline mode in preview mode
Diffstat (limited to 'sonar-batch')
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 { |