Browse Source

SONAR-6577 Offline mode in preview mode

tags/5.2-RC1
Duarte Meneses 9 years ago
parent
commit
8566436b50
62 changed files with 1512 additions and 589 deletions
  1. 4
    1
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/AnalysisProperties.java
  2. 1
    1
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java
  3. 10
    5
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginInstaller.java
  4. 5
    3
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginPredicate.java
  5. 6
    4
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/ExtensionInstaller.java
  6. 5
    7
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java
  7. 51
    0
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalMode.java
  8. 14
    6
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalSettings.java
  9. 2
    15
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/PersistentCacheProvider.java
  10. 39
    86
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java
  11. 182
    0
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/WSLoader.java
  12. 46
    0
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/WSLoaderGlobalProvider.java
  13. 6
    3
      sonar-batch/src/main/java/org/sonar/batch/cpd/index/IndexFactory.java
  14. 7
    5
      sonar-batch/src/main/java/org/sonar/batch/issue/tracking/DefaultServerLineHashesLoader.java
  15. 2
    2
      sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java
  16. 3
    2
      sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java
  17. 8
    5
      sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java
  18. 6
    5
      sonar-batch/src/main/java/org/sonar/batch/repository/DefaultGlobalRepositoriesLoader.java
  19. 11
    9
      sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoader.java
  20. 11
    14
      sonar-batch/src/main/java/org/sonar/batch/repository/DefaultServerIssuesLoader.java
  21. 16
    20
      sonar-batch/src/main/java/org/sonar/batch/repository/user/UserRepository.java
  22. 4
    3
      sonar-batch/src/main/java/org/sonar/batch/scan/ModuleSettings.java
  23. 17
    10
      sonar-batch/src/main/java/org/sonar/batch/scan/ProjectAnalysisMode.java
  24. 3
    4
      sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
  25. 4
    3
      sonar-batch/src/main/java/org/sonar/batch/scan/ProjectSettings.java
  26. 57
    0
      sonar-batch/src/main/java/org/sonar/batch/scan/WSLoaderProjectProvider.java
  27. 4
    3
      sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileBuilder.java
  28. 4
    3
      sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactory.java
  29. 4
    3
      sonar-batch/src/main/java/org/sonar/batch/scan/report/IssuesReports.java
  30. 12
    0
      sonar-batch/src/main/java/org/sonar/batch/util/BatchUtils.java
  31. 9
    8
      sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginInstallerTest.java
  32. 1
    2
      sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginPredicateTest.java
  33. 2
    2
      sonar-batch/src/test/java/org/sonar/batch/bootstrap/ExtensionInstallerTest.java
  34. 67
    0
      sonar-batch/src/test/java/org/sonar/batch/bootstrap/GlobalModeTest.java
  35. 13
    3
      sonar-batch/src/test/java/org/sonar/batch/bootstrap/GlobalSettingsTest.java
  36. 118
    0
      sonar-batch/src/test/java/org/sonar/batch/bootstrap/MockHttpServer.java
  37. 0
    15
      sonar-batch/src/test/java/org/sonar/batch/bootstrap/PersistentCacheProviderTest.java
  38. 28
    180
      sonar-batch/src/test/java/org/sonar/batch/bootstrap/ServerClientTest.java
  39. 76
    0
      sonar-batch/src/test/java/org/sonar/batch/bootstrap/WSLoaderGlobalProviderTest.java
  40. 214
    0
      sonar-batch/src/test/java/org/sonar/batch/bootstrap/WSLoaderTest.java
  41. 112
    0
      sonar-batch/src/test/java/org/sonar/batch/bootstrap/WSLoaderTestWithServer.java
  42. 4
    4
      sonar-batch/src/test/java/org/sonar/batch/cpd/index/IndexFactoryTest.java
  43. 11
    10
      sonar-batch/src/test/java/org/sonar/batch/issue/tracking/DefaultServerLineHashesLoaderTest.java
  44. 4
    4
      sonar-batch/src/test/java/org/sonar/batch/report/ReportPublisherTest.java
  45. 13
    13
      sonar-batch/src/test/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoaderTest.java
  46. 9
    9
      sonar-batch/src/test/java/org/sonar/batch/repository/DefaultServerIssuesLoaderTest.java
  47. 8
    9
      sonar-batch/src/test/java/org/sonar/batch/repository/user/UserRepositoryTest.java
  48. 25
    14
      sonar-batch/src/test/java/org/sonar/batch/scan/DefaultAnalysisModeTest.java
  49. 2
    3
      sonar-batch/src/test/java/org/sonar/batch/scan/ModuleSettingsTest.java
  50. 10
    6
      sonar-batch/src/test/java/org/sonar/batch/scan/ProjectSettingsTest.java
  51. 81
    0
      sonar-batch/src/test/java/org/sonar/batch/scan/WSLoaderProjectProviderTest.java
  52. 3
    3
      sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactoryTest.java
  53. 3
    2
      sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderTest.java
  54. 55
    17
      sonar-core/src/main/java/org/sonar/core/util/DefaultHttpDownloader.java
  55. 20
    1
      sonar-core/src/test/java/org/sonar/core/util/DefaultHttpDownloaderTest.java
  56. 25
    30
      sonar-home/src/main/java/org/sonar/home/cache/PersistentCache.java
  57. 1
    7
      sonar-home/src/main/java/org/sonar/home/cache/PersistentCacheBuilder.java
  58. 26
    0
      sonar-home/src/main/java/org/sonar/home/cache/PersistentCacheLoader.java
  59. 11
    25
      sonar-home/src/test/java/org/sonar/home/cache/PersistentCacheTest.java
  60. 5
    0
      sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
  61. 2
    0
      sonar-plugin-api/src/main/java/org/sonar/api/batch/AnalysisMode.java
  62. 10
    0
      sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java

+ 4
- 1
sonar-batch/src/main/java/org/sonar/batch/bootstrap/AnalysisProperties.java View 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);
}

+ 1
- 1
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java View 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

+ 10
- 5
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginInstaller.java View File

@@ -19,14 +19,18 @@
*/
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();

+ 5
- 3
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginPredicate.java View 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

+ 6
- 4
sonar-batch/src/main/java/org/sonar/batch/bootstrap/ExtensionInstaller.java View 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);
}


+ 5
- 7
sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java View 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();
}
}

+ 51
- 0
sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalMode.java View File

@@ -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");
}
}
}

+ 14
- 6
sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalSettings.java View 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));
}


+ 2
- 15
sonar-batch/src/main/java/org/sonar/batch/bootstrap/PersistentCacheProvider.java View 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);
}
}

+ 39
- 86
sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java View File

@@ -19,26 +19,27 @@
*/
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);
}
}

}

+ 182
- 0
sonar-batch/src/main/java/org/sonar/batch/bootstrap/WSLoader.java View File

@@ -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);
}
}
}

+ 46
- 0
sonar-batch/src/main/java/org/sonar/batch/bootstrap/WSLoaderGlobalProvider.java View File

@@ -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);
}
}

+ 6
- 3
sonar-batch/src/main/java/org/sonar/batch/cpd/index/IndexFactory.java View File

@@ -19,23 +19,26 @@
*/
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;
}

+ 7
- 5
sonar-batch/src/main/java/org/sonar/batch/issue/tracking/DefaultServerLineHashesLoader.java View File

@@ -19,18 +19,20 @@
*/
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();
}

+ 2
- 2
sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java View 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(),

+ 3
- 2
sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java View File

@@ -19,9 +19,10 @@
*/
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,

+ 8
- 5
sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java View File

@@ -19,8 +19,12 @@
*/
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) {

+ 6
- 5
sonar-batch/src/main/java/org/sonar/batch/repository/DefaultGlobalRepositoriesLoader.java View File

@@ -19,22 +19,23 @@
*/
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));
}

}

+ 11
- 9
sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoader.java View File

@@ -19,11 +19,13 @@
*/
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));
}

}

+ 11
- 14
sonar-batch/src/main/java/org/sonar/batch/repository/DefaultServerIssuesLoader.java View File

@@ -19,35 +19,32 @@
*/
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);

+ 16
- 20
sonar-batch/src/main/java/org/sonar/batch/repository/user/UserRepository.java View File

@@ -19,12 +19,13 @@
*/
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);

+ 4
- 3
sonar-batch/src/main/java/org/sonar/batch/scan/ModuleSettings.java View File

@@ -20,12 +20,13 @@
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;

sonar-batch/src/main/java/org/sonar/batch/bootstrap/DefaultAnalysisMode.java → sonar-batch/src/main/java/org/sonar/batch/scan/ProjectAnalysisMode.java View File

@@ -17,7 +17,9 @@
* 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;
package org.sonar.batch.scan;

import org.sonar.batch.bootstrap.AnalysisProperties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -31,21 +33,27 @@ import java.util.Map;
/**
* @since 4.0
*/
public class DefaultAnalysisMode implements AnalysisMode {
public class ProjectAnalysisMode implements AnalysisMode {

private static final Logger LOG = LoggerFactory.getLogger(DefaultAnalysisMode.class);
private static final Logger LOG = LoggerFactory.getLogger(ProjectAnalysisMode.class);

private boolean preview;
private boolean incremental;
private boolean quick;
private boolean mediumTestMode;

public DefaultAnalysisMode(Map<String, String> props) {
init(props);
public ProjectAnalysisMode(AnalysisProperties props) {
init(props.properties());
}

@Override
public boolean isPreview() {
return preview || incremental;
return preview || incremental || quick;
}

@Override
public boolean isQuick() {
return quick;
}

@Override
@@ -66,20 +74,19 @@ public class DefaultAnalysisMode implements AnalysisMode {
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");
}
// To stay compatible with plugins that use the old property to check mode
if (incremental || preview) {
props.put(CoreProperties.DRY_RUN, "true");
}
}

}

+ 3
- 4
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java View 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();
}
}

+ 4
- 3
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectSettings.java View File

@@ -20,13 +20,14 @@
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));

+ 57
- 0
sonar-batch/src/main/java/org/sonar/batch/scan/WSLoaderProjectProvider.java View File

@@ -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);
}
}

+ 4
- 3
sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileBuilder.java View 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;

+ 4
- 3
sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactory.java View File

@@ -19,12 +19,13 @@
*/
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;

+ 4
- 3
sonar-batch/src/main/java/org/sonar/batch/scan/report/IssuesReports.java View File

@@ -19,16 +19,17 @@
*/
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;
}

+ 12
- 0
sonar-batch/src/main/java/org/sonar/batch/util/BatchUtils.java View 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);
}
}
}

+ 9
- 8
sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginInstallerTest.java View 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();
}
}

+ 1
- 2
sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginPredicateTest.java View 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() {

+ 2
- 2
sonar-batch/src/test/java/org/sonar/batch/bootstrap/ExtensionInstallerTest.java View 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

+ 67
- 0
sonar-batch/src/test/java/org/sonar/batch/bootstrap/GlobalModeTest.java View File

@@ -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);
}
}

+ 13
- 3
sonar-batch/src/test/java/org/sonar/batch/bootstrap/GlobalSettingsTest.java View File

@@ -19,7 +19,11 @@
*/
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() {

+ 118
- 0
sonar-batch/src/test/java/org/sonar/batch/bootstrap/MockHttpServer.java View File

@@ -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();
}

}

+ 0
- 15
sonar-batch/src/test/java/org/sonar/batch/bootstrap/PersistentCacheProviderTest.java View 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() {
}
}

+ 28
- 180
sonar-batch/src/test/java/org/sonar/batch/bootstrap/ServerClientTest.java View File

@@ -19,30 +19,18 @@
*/
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);
}

}

}

+ 76
- 0
sonar-batch/src/test/java/org/sonar/batch/bootstrap/WSLoaderGlobalProviderTest.java View File

@@ -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);
}
}

+ 214
- 0
sonar-batch/src/test/java/org/sonar/batch/bootstrap/WSLoaderTest.java View File

@@ -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);
}
}

+ 112
- 0
sonar-batch/src/test/java/org/sonar/batch/bootstrap/WSLoaderTestWithServer.java View File

@@ -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);
}

}

+ 4
- 4
sonar-batch/src/test/java/org/sonar/batch/cpd/index/IndexFactoryTest.java View File

@@ -19,14 +19,14 @@
*/
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);
}

+ 11
- 10
sonar-batch/src/test/java/org/sonar/batch/issue/tracking/DefaultServerLineHashesLoaderTest.java View File

@@ -19,12 +19,13 @@
*/
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);


+ 4
- 4
sonar-batch/src/test/java/org/sonar/batch/report/ReportPublisherTest.java View 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"));
}

+ 13
- 13
sonar-batch/src/test/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoaderTest.java View File

@@ -19,16 +19,16 @@
*/
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");
}

}

+ 9
- 9
sonar-batch/src/test/java/org/sonar/batch/repository/DefaultServerIssuesLoaderTest.java View File

@@ -19,17 +19,17 @@
*/
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>() {

+ 8
- 9
sonar-batch/src/test/java/org/sonar/batch/repository/user/UserRepositoryTest.java View File

@@ -19,15 +19,15 @@
*/
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"));
}

sonar-batch/src/test/java/org/sonar/batch/bootstrap/DefaultAnalysisModeTest.java → sonar-batch/src/test/java/org/sonar/batch/scan/DefaultAnalysisModeTest.java View File

@@ -17,14 +17,15 @@
* 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;
package org.sonar.batch.scan;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
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;
@@ -33,12 +34,12 @@ public class DefaultAnalysisModeTest {

@Test
public void regular_analysis_by_default() {
DefaultAnalysisMode mode = new DefaultAnalysisMode(Collections.<String, String>emptyMap());
ProjectAnalysisMode mode = new ProjectAnalysisMode(new AnalysisProperties(Collections.<String, String>emptyMap()));

assertThat(mode.isPreview()).isFalse();
assertThat(mode.isIncremental()).isFalse();

mode = new DefaultAnalysisMode(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, "pouet"));
mode = createMode(CoreProperties.ANALYSIS_MODE, "pouet");

assertThat(mode.isPreview()).isFalse();
assertThat(mode.isIncremental()).isFalse();
@@ -46,7 +47,7 @@ public class DefaultAnalysisModeTest {

@Test
public void support_analysis_mode() {
DefaultAnalysisMode mode = new DefaultAnalysisMode(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ANALYSIS));
ProjectAnalysisMode mode = createMode(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ANALYSIS);

assertThat(mode.isPreview()).isFalse();
assertThat(mode.isIncremental()).isFalse();
@@ -54,31 +55,41 @@ public class DefaultAnalysisModeTest {

@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);
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(props.get(CoreProperties.DRY_RUN)).isEqualTo("true");
assertThat(mode.isPreview()).isTrue();
assertThat(mode.isIncremental()).isFalse();
assertThat(mode.isQuick()).isTrue();
}

@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);
ProjectAnalysisMode mode = createMode(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_INCREMENTAL);

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")));
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));
}
}

+ 2
- 3
sonar-batch/src/test/java/org/sonar/batch/scan/ModuleSettingsTest.java View 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

+ 10
- 6
sonar-batch/src/test/java/org/sonar/batch/scan/ProjectSettingsTest.java View File

@@ -19,8 +19,12 @@
*/
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.");

+ 81
- 0
sonar-batch/src/test/java/org/sonar/batch/scan/WSLoaderProjectProviderTest.java View File

@@ -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);
}
}

+ 3
- 3
sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactoryTest.java View File

@@ -19,14 +19,14 @@
*/
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());

+ 3
- 2
sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderTest.java View 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 {

+ 55
- 17
sonar-core/src/main/java/org/sonar/core/util/DefaultHttpDownloader.java View 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);

+ 20
- 1
sonar-core/src/test/java/org/sonar/core/util/DefaultHttpDownloaderTest.java View 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));

+ 25
- 30
sonar-home/src/main/java/org/sonar/home/cache/PersistentCache.java View 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
*/

+ 1
- 7
sonar-home/src/main/java/org/sonar/home/cache/PersistentCacheBuilder.java View 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");


+ 26
- 0
sonar-home/src/main/java/org/sonar/home/cache/PersistentCacheLoader.java View File

@@ -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;
}

+ 11
- 25
sonar-home/src/test/java/org/sonar/home/cache/PersistentCacheTest.java View 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);
}


+ 5
- 0
sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java View 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

+ 2
- 0
sonar-plugin-api/src/main/java/org/sonar/api/batch/AnalysisMode.java View File

@@ -29,5 +29,7 @@ public interface AnalysisMode {
boolean isPreview();

boolean isIncremental();
boolean isQuick();

}

+ 10
- 0
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java View 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 {

Loading…
Cancel
Save