import java.io.File;
import java.io.IOException;
+import static org.junit.Assert.fail;
+
import static org.assertj.core.api.Assertions.assertThat;
public class CacheTest extends RunnerTestCase {
build = createRunner("issues", true, "java-sample");
try {
result = orchestrator.executeBuild(build);
+ fail("exception expected");
} catch (BuildFailureException e) {
- assertThat(e.getResult().getLogs()).contains("and data is not cached");
+ // this message is specific to the server_first cache strategy
+ assertThat(e.getResult().getLogs()).contains("can not be reached, trying cache");
+ assertThat(e.getResult().getLogs()).contains("can not be reached and data is not cached");
}
}
start(false);
}
- public void start(boolean forceSync) {
+ public void start(boolean preferCache) {
initGlobalDefaultValues();
- doStart(forceSync);
+ doStart(preferCache);
}
/**
}
}
- protected void doStart(boolean forceSync) {
+ protected void doStart(boolean preferCache) {
checkLauncherDoesntExist();
ClassloadRules rules = new ClassloadRules(classloaderMask, classloaderUnmask);
- launcher = launcherFactory.createLauncher(globalProperties(), rules);
+ launcher = launcherFactory.createLauncher(globalProperties(), rules, preferCache);
if (VersionUtils.isAtLeast52(launcher.getVersion())) {
launcher.start(globalProperties(), new org.sonar.runner.batch.LogOutput() {
logOutput.log(formattedMessage, LogOutput.Level.valueOf(level.name()));
}
- }, forceSync);
+ }, preferCache);
}
}
return FileCache.create(cacheDir, logger);
}
- private File findHome() {
+ private static File findHome() {
String path = System.getenv("SONAR_USER_HOME");
if (path == null) {
// Default
return classloader;
}
- public IsolatedLauncher createLauncher(Properties props, ClassloadRules rules) {
+ public IsolatedLauncher createLauncher(Properties props, ClassloadRules rules, boolean preferCache) {
if (props.containsKey(InternalProperties.RUNNER_DUMP_TO_FILE)) {
String version = props.getProperty(InternalProperties.RUNNER_VERSION_SIMULATION);
if (version == null) {
}
return new SimulatedLauncher(version, logger);
}
- ServerConnection serverConnection = ServerConnection.create(props, getCache(props), logger);
+ ServerConnection serverConnection = ServerConnection.create(props, getCache(props), logger, preferCache);
JarDownloader jarDownloader = new JarDownloader(serverConnection, logger, props);
return createLauncher(jarDownloader, rules);
try {
List<File> files = new ArrayList<File>();
logger.debug("Get bootstrap index...");
- String libs = connection.downloadStringCache(BOOTSTRAP_INDEX_PATH);
+ String libs = connection.loadString(BOOTSTRAP_INDEX_PATH);
logger.debug("Get bootstrap completed");
String[] lines = libs.split("[\r\n]+");
BatchFileDownloader batchFileDownloader = new BatchFileDownloader(connection);
private final String userAgent;
private final PersistentCache wsCache;
- private final boolean isCacheEnable;
+ private final boolean preferCache;
private final Logger logger;
+ private final boolean isCacheEnabled;
- private ServerConnection(String serverUrl, String app, String appVersion, boolean isCacheEnable, PersistentCache cache, Logger logger) {
+ private ServerConnection(String serverUrl, String app, String appVersion, boolean preferCache, boolean cacheEnabled, PersistentCache cache, Logger logger) {
+ this.isCacheEnabled = cacheEnabled;
this.logger = logger;
this.serverUrl = removeEndSlash(serverUrl);
this.userAgent = app + "/" + appVersion;
this.wsCache = cache;
- this.isCacheEnable = isCacheEnable;
+ this.preferCache = preferCache;
}
private static String removeEndSlash(String url) {
return url.endsWith("/") ? url.substring(0, url.length() - 1) : url;
}
- static ServerConnection create(Properties properties, PersistentCache cache, Logger logger) {
+ static ServerConnection create(Properties properties, PersistentCache cache, Logger logger, boolean preferCache) {
String serverUrl = properties.getProperty("sonar.host.url");
String app = properties.getProperty(InternalProperties.RUNNER_APP);
String appVersion = properties.getProperty(InternalProperties.RUNNER_APP_VERSION);
boolean enableCache = isCacheEnabled(properties);
- return new ServerConnection(serverUrl, app, appVersion, enableCache, cache, logger);
+ return new ServerConnection(serverUrl, app, appVersion, preferCache, enableCache, cache, logger);
}
private static boolean isCacheEnabled(Properties properties) {
httpRequest.receive(toFile);
} catch (Exception e) {
- if (e.getCause() instanceof ConnectException || e.getCause() instanceof UnknownHostException) {
+ if (isCausedByConnection(e)) {
logger.error(MessageFormat.format(SONAR_SERVER_CAN_NOT_BE_REACHED, serverUrl));
}
FileUtils.deleteQuietly(toFile);
}
/**
- * Tries to fetch from server and falls back to cache. If both attempts fail, it throws the exception
- * linked to the server connection failure.
+ * Tries to fetch from cache and server. If both attempts fail, it throws the exception linked to the server connection failure.
*/
- String downloadStringCache(String path) throws IOException {
+ String loadString(String path) throws IOException {
String fullUrl = serverUrl + path;
+
+ if (isCacheEnabled && preferCache) {
+ return tryCacheFirst(fullUrl);
+ } else {
+ return tryServerFirst(fullUrl, isCacheEnabled);
+ }
+
+ }
+
+ private String tryCacheFirst(String fullUrl) throws IOException {
+ String cached = getFromCache(fullUrl);
+ if (cached != null) {
+ return cached;
+ }
+
try {
- return downloadString(fullUrl, isCacheEnable);
+ return downloadString(fullUrl, preferCache);
+ } catch (Exception e) {
+ logger.error(MessageFormat.format("Data is not cached and " + SONAR_SERVER_CAN_NOT_BE_REACHED, serverUrl));
+ throw e;
+ }
+ }
+
+ private String tryServerFirst(String fullUrl, boolean cacheEnabled) throws IOException {
+ try {
+ return downloadString(fullUrl, cacheEnabled);
} catch (HttpRequest.HttpRequestException e) {
- if (isCausedByConnection(e) && isCacheEnable) {
- return fallbackToCache(fullUrl, e);
+ if (cacheEnabled && isCausedByConnection(e)) {
+ logger.info(MessageFormat.format(SONAR_SERVER_CAN_NOT_BE_REACHED + ", trying cache", serverUrl));
+ String cached = getFromCache(fullUrl);
+ if (cached != null) {
+ return cached;
+ }
+ logger.error(MessageFormat.format(SONAR_SERVER_CAN_NOT_BE_REACHED + " and data is not cached", serverUrl));
+ throw e;
}
logger.error(MessageFormat.format(SONAR_SERVER_CAN_NOT_BE_REACHED, serverUrl));
e.getCause() instanceof java.net.SocketTimeoutException;
}
- private String fallbackToCache(String fullUrl, HttpRequest.HttpRequestException originalException) {
- logger.info(MessageFormat.format(SONAR_SERVER_CAN_NOT_BE_REACHED + ", trying cache", serverUrl));
-
+ private String getFromCache(String fullUrl) {
try {
- String cached = wsCache.getString(fullUrl);
- if (cached != null) {
- return cached;
- }
- logger.error(MessageFormat.format(SONAR_SERVER_CAN_NOT_BE_REACHED + " and data is not cached", serverUrl));
- throw originalException;
+ return wsCache.getString(fullUrl);
} catch (IOException e) {
throw new IllegalStateException("Failed to access cache", e);
}
-
}
private HttpRequest newHttpRequest(URL url) {
}
@Override
- public void start(Properties properties, LogOutput logOutput, boolean forceSync) {
+ public void start(Properties properties, LogOutput logOutput, boolean preferCache) {
globalProperties = properties;
}
import java.util.List;
import java.util.Properties;
+import static org.mockito.Matchers.anyBoolean;
+
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.argThat;
batchLauncher = mock(IsolatedLauncherFactory.class);
launcher = mock(IsolatedLauncher.class);
when(launcher.getVersion()).thenReturn("5.2");
- when(batchLauncher.createLauncher(any(Properties.class), any(ClassloadRules.class))).thenReturn(launcher);
+ when(batchLauncher.createLauncher(any(Properties.class), any(ClassloadRules.class), anyBoolean())).thenReturn(launcher);
runner = new EmbeddedRunner(batchLauncher, mock(Logger.class), mock(LogOutput.class));
}
public boolean matches(Object o) {
return "foo".equals(((Properties) o).getProperty("sonar.projectKey"));
}
- }), any(ClassloadRules.class));
+ }), any(ClassloadRules.class), anyBoolean());
// it should have added a few properties to analysisProperties, and have merged global props
final String[] mustHaveKeys = {"sonar.working.directory", "sonar.sourceEncoding", "sonar.projectBaseDir",
public boolean matches(Object o) {
return "foo".equals(((Properties) o).getProperty("sonar.projectKey"));
}
- }), any(ClassloadRules.class));
+ }), any(ClassloadRules.class), anyBoolean());
// it should have added a few properties to analysisProperties
final String[] mustHaveKeys = {"sonar.working.directory", "sonar.sourceEncoding", "sonar.projectBaseDir"};
public boolean matches(Object o) {
return "foo".equals(((Properties) o).getProperty("sonar.projectKey"));
}
- }), any(ClassloadRules.class));
+ }), any(ClassloadRules.class), anyBoolean());
verify(launcher).execute(argThat(new ArgumentMatcher<Properties>() {
@Override
public static IssueListener listener = null;
@Override
- public void start(Properties properties, LogOutput logger, boolean forceSync) {
+ public void start(Properties properties, LogOutput logger, boolean preferCache) {
}
@Override
File batchJar = temp.newFile("sonar-runner-batch.jar");
when(jarExtractor.extractToTemp("sonar-runner-batch")).thenReturn(batchJar);
// index of the files to download
- when(connection.downloadStringCache("/batch_bootstrap/index")).thenReturn(
+ when(connection.loadString("/batch_bootstrap/index")).thenReturn(
"cpd.jar|CA124VADFSDS\n" +
"squid.jar|34535FSFSDF\n");
List<File> files = jars35.download();
assertThat(files).isNotNull();
- verify(connection, times(1)).downloadStringCache("/batch_bootstrap/index");
+ verify(connection, times(1)).loadString("/batch_bootstrap/index");
verifyNoMoreInteractions(connection);
verify(fileCache, times(1)).get(eq("cpd.jar"), eq("CA124VADFSDS"), any(FileCache.Downloader.class));
verify(fileCache, times(1)).get(eq("squid.jar"), eq("34535FSFSDF"), any(FileCache.Downloader.class));
File batchJar = temp.newFile("sonar-runner-batch.jar");
when(jarExtractor.extractToTemp("sonar-runner-batch")).thenReturn(batchJar);
// index of the files to download
- when(connection.downloadStringCache("/batch_bootstrap/index")).thenThrow(new IllegalStateException());
+ when(connection.loadString("/batch_bootstrap/index")).thenThrow(new IllegalStateException());
Jars jars35 = new Jars(fileCache, connection, jarExtractor, mock(Logger.class));
try {
public void continue_if_cache_put_fails() throws Exception {
cache = mock(PersistentCache.class);
doThrow(IOException.class).when(cache).put(anyString(), any(byte[].class));
- ServerConnection connection = createSimpleServerConnection(httpServer.url(), null, true);
- String response = connection.downloadStringCache("/batch/index.txt");
+ ServerConnection connection = createSimpleServerConnection(httpServer.url(), null, true, false);
+ String response = connection.loadString("/batch/index.txt");
assertThat(response).isEqualTo("abcde");
verify(logger).warn(startsWith("Failed to cache WS call:"));
@Test
public void should_download_to_string() throws Exception {
ServerConnection connection = createSimpleServerConnection(httpServer.url(), null);
- String response = connection.downloadStringCache("/batch/index.txt");
+ String response = connection.loadString("/batch/index.txt");
assertThat(response).isEqualTo("abcde");
}
+
+ @Test
+ public void test_prefer_cache() throws IOException {
+ File cacheDir = cache.getDirectory().toFile();
+
+ ServerConnection connection = createSimpleServerConnection(httpServer.url() + "/", null, true, true);
+
+ //not cached
+ assertThat(cacheDir.list().length).isEqualTo(0);
+ String str = connection.loadString("/batch/index.txt");
+
+ //cached
+ assertThat(str).isEqualTo("abcde");
+ assertThat(cacheDir.list().length).isEqualTo(1);
+
+ httpServer.setMockResponseData("request2");
+ //got response in cached
+ str = connection.loadString("/batch/index.txt");
+ assertThat(str).isEqualTo("abcde");
+ }
+
+ @Test
+ public void test_dont_prefer_cache() throws IOException {
+ File cacheDir = cache.getDirectory().toFile();
+
+ ServerConnection connection = createSimpleServerConnection(httpServer.url() + "/", null, true, false);
+
+ //not cached
+ assertThat(cacheDir.list().length).isEqualTo(0);
+ String str = connection.loadString("/batch/index.txt");
+
+ //cached
+ assertThat(str).isEqualTo("abcde");
+ assertThat(cacheDir.list().length).isEqualTo(1);
+
+ httpServer.setMockResponseData("request2");
+ //got response in cached
+ str = connection.loadString("/batch/index.txt");
+ assertThat(str).isEqualTo("request2");
+
+ httpServer.after();
+ str = connection.loadString("/batch/index.txt");
+ assertThat(str).isEqualTo("request2");
+ }
@Test
public void should_download_to_file() throws Exception {
}
@Test
- public void should_throw_original_exception_fallback() throws IOException {
+ public void should_throw_connection_exception_fallback_to_cache() throws IOException {
cache = mock(PersistentCache.class);
- ServerConnection connection = createSimpleServerConnection("http://localhost", NetworkUtil.getNextAvailablePort(), true);
+ ServerConnection connection = createSimpleServerConnection("http://localhost", NetworkUtil.getNextAvailablePort(),
+ true, false);
try {
- connection.downloadStringCache("/batch/index.txt");
+ connection.loadString("/batch/index.txt");
fail();
} catch (HttpRequest.HttpRequestException e) {
verify(cache).getString(anyString());
public void should_cache_jar_list() throws Exception {
File cacheDir = cache.getDirectory().toFile();
- ServerConnection connection = createSimpleServerConnection(httpServer.url() + "/", null, true);
+ ServerConnection connection = createSimpleServerConnection(httpServer.url() + "/", null, true, false);
assertThat(cacheDir.list().length).isEqualTo(0);
- String str = connection.downloadStringCache("/batch/index.txt");
+ String str = connection.loadString("/batch/index.txt");
assertThat(str).isEqualTo("abcde");
assertThat(cacheDir.list().length).isEqualTo(1);
httpServer.after();
- str = connection.downloadStringCache("/batch/index.txt");
+ str = connection.loadString("/batch/index.txt");
assertThat(str).isEqualTo("abcde");
}
@Test
- public void should_throw_connection_exception_() throws IOException {
+ public void should_throw_connection_exception() throws IOException {
File cacheDir = cache.getDirectory().toFile();
ServerConnection connection = createSimpleServerConnection(httpServer.url() + "/", null);
assertThat(cacheDir.list().length).isEqualTo(0);
- String str = connection.downloadStringCache("/batch/index.txt");
+ String str = connection.loadString("/batch/index.txt");
assertThat(str).isEqualTo("abcde");
httpServer.after();
try {
- connection.downloadStringCache("/batch/index.txt");
+ connection.loadString("/batch/index.txt");
fail("exception expected");
} catch (HttpRequest.HttpRequestException e) {
// expected
ServerConnection connection = createSimpleServerConnection(httpServer.url() + "/", null);
assertThat(cacheDir.list().length).isEqualTo(0);
- String str = connection.downloadStringCache("/batch/index.txt");
+ String str = connection.loadString("/batch/index.txt");
assertThat(str).isEqualTo("abcde");
assertThat(cacheDir.list().length).isEqualTo(0);
httpServer.setMockResponseData("request2");
- str = connection.downloadStringCache("/batch/index.txt");
+ str = connection.loadString("/batch/index.txt");
assertThat(str).isEqualTo("request2");
}
ServerConnection connection = createSimpleServerConnection("http://localhost", NetworkUtil.getNextAvailablePort());
try {
- connection.downloadStringCache("/batch/index.txt");
+ connection.loadString("/batch/index.txt");
fail();
} catch (Exception e) {
// success
}
private ServerConnection createSimpleServerConnection(String url, Integer port) {
- return createSimpleServerConnection(url, port, false);
+ return createSimpleServerConnection(url, port, false, false);
}
- private ServerConnection createSimpleServerConnection(String url, Integer port, boolean issuesMode) {
+ private ServerConnection createSimpleServerConnection(String url, Integer port, boolean issuesMode, boolean preferCache) {
httpServer.setMockResponseData("abcde");
String fullUrl = port == null ? url : url + ":" + port;
Properties props = new Properties();
if (issuesMode) {
props.setProperty("sonar.analysis.mode", "issues");
}
- return ServerConnection.create(props, cache, logger);
+ return ServerConnection.create(props, cache, logger, preferCache);
}
}
public interface IsolatedLauncher {
- void start(Properties properties, LogOutput logOutput, boolean forceSync);
+ void start(Properties properties, LogOutput logOutput, boolean preferCache);
void stop();
private Batch batch = null;
@Override
- public void start(Properties globalProperties, org.sonar.runner.batch.LogOutput logOutput, boolean forceSync) {
+ public void start(Properties globalProperties, org.sonar.runner.batch.LogOutput logOutput, boolean preferCache) {
batch = createBatch(globalProperties, logOutput, null);
- batch.start(forceSync);
+ batch.start(preferCache);
}
@Override