diff options
author | Duarte Meneses <duarte.meneses@sonarsource.com> | 2015-06-18 15:23:54 +0200 |
---|---|---|
committer | Duarte Meneses <duarte.meneses@sonarsource.com> | 2015-06-24 14:34:21 +0200 |
commit | a3f2f42774fc6f83b8bb743743a092ffc7b9bb50 (patch) | |
tree | 035aa4f53d80412c126f2de5f0df0647e4e70283 /sonar-batch | |
parent | 62bded91ea52f42a71ad7d7624d5447b1a101420 (diff) | |
download | sonarqube-a3f2f42774fc6f83b8bb743743a092ffc7b9bb50.tar.gz sonarqube-a3f2f42774fc6f83b8bb743743a092ffc7b9bb50.zip |
SONAR-6649 Move initialization of persistit cache to global context
Diffstat (limited to 'sonar-batch')
18 files changed, 587 insertions, 173 deletions
diff --git a/sonar-batch/pom.xml b/sonar-batch/pom.xml index c1b2b197c32..db21e928a97 100644 --- a/sonar-batch/pom.xml +++ b/sonar-batch/pom.xml @@ -25,7 +25,7 @@ <scope>provided</scope> </dependency> <dependency> - <groupId>org.codehaus.sonar</groupId> + <groupId>org.sonarsource</groupId> <artifactId>sonar-persistit</artifactId> </dependency> <dependency> diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java index 08d9cbb9a7c..94d6effc42d 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java @@ -29,7 +29,6 @@ import org.sonar.api.Plugin; import org.sonar.api.utils.Durations; import org.sonar.api.utils.System2; import org.sonar.api.utils.UriReader; -import org.sonar.api.utils.internal.TempFolderCleaner; import org.sonar.batch.components.PastSnapshotFinder; import org.sonar.batch.deprecated.components.PastSnapshotFinderByDate; import org.sonar.batch.deprecated.components.PastSnapshotFinderByDays; @@ -67,13 +66,14 @@ import org.sonar.jpa.session.JpaDatabaseSession; public class GlobalContainer extends ComponentContainer { private final Map<String, String> bootstrapProperties; + private PersistentCacheProvider persistentCacheProvider; private GlobalContainer(Map<String, String> bootstrapProperties) { super(); this.bootstrapProperties = bootstrapProperties; } - public static GlobalContainer create(Map<String, String> bootstrapProperties, List extensions) { + public static GlobalContainer create(Map<String, String> bootstrapProperties, List<?> extensions) { GlobalContainer container = new GlobalContainer(bootstrapProperties); container.add(extensions); return container; @@ -92,6 +92,8 @@ public class GlobalContainer extends ComponentContainer { } private void addBootstrapComponents() { + persistentCacheProvider = new PersistentCacheProvider(); + add( // plugins BatchPluginRepository.class, @@ -107,11 +109,10 @@ public class GlobalContainer extends ComponentContainer { Logback.class, DefaultServer.class, new TempFolderProvider(), - TempFolderCleaner.class, DefaultHttpDownloader.class, UriReader.class, new FileCacheProvider(), - new PersistentCacheProvider(), + persistentCacheProvider, System2.INSTANCE, DefaultI18n.class, Durations.class, @@ -124,7 +125,7 @@ public class GlobalContainer extends ComponentContainer { addIfMissing(DefaultServerLineHashesLoader.class, ServerLineHashesLoader.class); } - public void addIfMissing(Object object, Class objectType) { + public void addIfMissing(Object object, Class<?> objectType) { if (getComponentByType(objectType) == null) { add(object); } @@ -166,7 +167,7 @@ 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(); } - } diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/LifecycleProviderAdapter.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/LifecycleProviderAdapter.java new file mode 100644 index 00000000000..009aadad61d --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/LifecycleProviderAdapter.java @@ -0,0 +1,85 @@ +/* + * 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.lifecycle.ReflectionLifecycleStrategy; +import org.picocontainer.monitors.NullComponentMonitor; + +import org.picocontainer.LifecycleStrategy; +import org.picocontainer.PicoContainer; +import org.picocontainer.ComponentLifecycle; +import org.picocontainer.injectors.ProviderAdapter; +import org.picocontainer.Startable; + +public abstract class LifecycleProviderAdapter extends ProviderAdapter implements Startable, ComponentLifecycle<Object> { + private LifecycleStrategy lifecycleStrategy; + protected Object instance; + + public LifecycleProviderAdapter() { + this(new ReflectionLifecycleStrategy(new NullComponentMonitor())); + } + + public LifecycleProviderAdapter(LifecycleStrategy lifecycleStrategy) { + this.lifecycleStrategy = lifecycleStrategy; + } + + @Override + public final void start() { + if (instance != null) { + lifecycleStrategy.start(instance); + } + } + + @Override + public final void stop() { + if (instance != null) { + lifecycleStrategy.stop(instance); + } + } + + @Override + public void start(PicoContainer container) { + start(); + started = true; + } + + @Override + public void stop(PicoContainer container) { + stop(); + started = false; + } + + @Override + public void dispose(PicoContainer container) { + } + + @Override + public boolean componentHasLifecycle() { + return true; + } + + @Override + public boolean isStarted() { + return started; + } + + private boolean started = false; + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/PersistentCacheProvider.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/PersistentCacheProvider.java index 9ca57407d6c..6821333b27f 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/PersistentCacheProvider.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/PersistentCacheProvider.java @@ -20,27 +20,23 @@ package org.sonar.batch.bootstrap; import org.sonar.home.log.Slf4jLog; - import org.sonar.home.cache.PersistentCacheBuilder; import org.picocontainer.injectors.ProviderAdapter; import java.nio.file.Paths; +import java.util.Map; import org.sonar.home.cache.PersistentCache; public class PersistentCacheProvider extends ProviderAdapter { private PersistentCache cache; - public PersistentCache provide(BootstrapProperties props) { + public PersistentCache provide(UserProperties props) { if (cache == null) { PersistentCacheBuilder builder = new PersistentCacheBuilder(); builder.setLog(new Slf4jLog(PersistentCache.class)); - String enableCache = props.property("sonar.enableHttpCache"); - - if (!"true".equals(enableCache)) { - builder.forceUpdate(true); - } + builder.forceUpdate(isForceUpdate(props.properties())); String home = props.property("sonar.userHome"); if (home != null) { @@ -51,4 +47,15 @@ public class PersistentCacheProvider extends ProviderAdapter { } 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); + } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectTempFolderProvider.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectTempFolderProvider.java index e83013371ad..bcaf8ec8666 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectTempFolderProvider.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectTempFolderProvider.java @@ -19,34 +19,33 @@ */ package org.sonar.batch.bootstrap; -import org.sonar.api.utils.ProjectTempFolder; - -import org.apache.commons.io.FileUtils; +import org.sonar.api.utils.TempFolder; import org.apache.commons.lang.StringUtils; -import org.picocontainer.injectors.ProviderAdapter; import org.sonar.api.CoreProperties; import org.sonar.api.utils.internal.DefaultTempFolder; -import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; -public class ProjectTempFolderProvider extends ProviderAdapter { +public class ProjectTempFolderProvider extends LifecycleProviderAdapter { static final String TMP_NAME = ".sonartmp"; - private ProjectTempFolder projectTempFolder; + private DefaultTempFolder projectTempFolder; - public ProjectTempFolder provide(BootstrapProperties bootstrapProps) { + public TempFolder provide(BootstrapProperties bootstrapProps) { if (projectTempFolder == null) { String workingDirPath = StringUtils.defaultIfBlank(bootstrapProps.property(CoreProperties.WORKING_DIRECTORY), CoreProperties.WORKING_DIRECTORY_DEFAULT_VALUE); - File workingDir = new File(workingDirPath).getAbsoluteFile(); - File tempDir = new File(workingDir, TMP_NAME); + Path workingDir = Paths.get(workingDirPath); + Path tempDir = workingDir.resolve(TMP_NAME).normalize(); try { - FileUtils.forceMkdir(tempDir); + Files.createDirectories(tempDir); } catch (IOException e) { throw new IllegalStateException("Unable to create root temp directory " + tempDir, e); } - projectTempFolder = new DefaultTempFolder(tempDir, true); + projectTempFolder = new DefaultTempFolder(tempDir.toFile(), true); + this.instance = projectTempFolder; } return projectTempFolder; } - } diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TempFolderProvider.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TempFolderProvider.java index 971a4a2b1f5..b1ee968f344 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TempFolderProvider.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TempFolderProvider.java @@ -19,20 +19,28 @@ */ package org.sonar.batch.bootstrap; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.apache.commons.io.FileUtils; import org.sonar.api.utils.TempFolder; -import org.picocontainer.injectors.ProviderAdapter; import org.apache.commons.lang.StringUtils; import org.sonar.api.CoreProperties; import org.sonar.api.utils.internal.DefaultTempFolder; import java.io.IOException; +import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.concurrent.TimeUnit; -public class TempFolderProvider extends ProviderAdapter { - static final String TMP_NAME = ".sonartmp"; - private TempFolder tempFolder; +public class TempFolderProvider extends LifecycleProviderAdapter { + private static final Logger LOG = Loggers.get(TempFolderProvider.class); + private static final long CLEAN_MAX_AGE = TimeUnit.DAYS.toMillis(21); + static final String TMP_NAME_PREFIX = ".sonartmp_"; + + private DefaultTempFolder tempFolder; public TempFolder provide(BootstrapProperties bootstrapProps) { if (tempFolder == null) { @@ -45,13 +53,20 @@ public class TempFolderProvider extends ProviderAdapter { workingPath = home.resolve(workingPath).normalize(); } - Path tempDir = workingPath.resolve(TMP_NAME); + try { + cleanTempFolders(workingPath); + } catch (IOException e) { + LOG.warn("failed to clean global working directory: " + e.getMessage()); + } + + Path tempDir = workingPath.resolve(TMP_NAME_PREFIX + System.currentTimeMillis()); try { Files.createDirectories(tempDir); } catch (IOException e) { throw new IllegalStateException("Unable to create root temp directory " + tempDir, e); } tempFolder = new DefaultTempFolder(tempDir.toFile(), true); + this.instance = tempFolder; } return tempFolder; } @@ -72,4 +87,41 @@ public class TempFolderProvider extends ProviderAdapter { return Paths.get(home, ".sonar"); } + private static void cleanTempFolders(Path path) throws IOException { + if (Files.exists(path)) { + try (DirectoryStream<Path> stream = Files.newDirectoryStream(path, new CleanFilter())) { + for (Path p : stream) { + FileUtils.deleteQuietly(p.toFile()); + } + } + } + } + + private static class CleanFilter implements DirectoryStream.Filter<Path> { + @Override + public boolean accept(Path e) throws IOException { + if (!Files.isDirectory(e)) { + return false; + } + + if (!e.getFileName().toString().startsWith(TMP_NAME_PREFIX)) { + return false; + } + + long threshold = System.currentTimeMillis() - CLEAN_MAX_AGE; + + // we could also check the timestamp in the name, instead + BasicFileAttributes attrs; + + try { + attrs = Files.readAttributes(e, BasicFileAttributes.class); + } catch (IOException ioe) { + LOG.warn("couldn't read file attributes for " + e + " : " + ioe.getMessage()); + return false; + } + + long creationTime = attrs.creationTime().toMillis(); + return creationTime < threshold; + } + } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/LogCallbackAppender.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/LogCallbackAppender.java index 6c216410d13..73730de7df5 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/LogCallbackAppender.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/LogCallbackAppender.java @@ -38,11 +38,11 @@ public class LogCallbackAppender extends UnsynchronizedAppenderBase<ILoggingEven @Override protected void append(ILoggingEvent event) { - target.log(event.getFormattedMessage(), translate(event.getLevel())); + target.log(event.getMessage(), translate(event.getLevel())); } - - private LogListener.Level translate(Level level) { - switch(level.toInt()) { + + private static LogListener.Level translate(Level level) { + switch (level.toInt()) { case Level.ERROR_INT: return LogListener.Level.ERROR; case Level.WARN_INT: diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/Caches.java b/sonar-batch/src/main/java/org/sonar/batch/index/Caches.java index 209ec7792aa..4f0bf342693 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/Caches.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/Caches.java @@ -37,17 +37,21 @@ import org.sonar.api.batch.BatchSide; @BatchSide public class Caches implements Startable { - private final Map<String, Exchange> caches = Maps.newHashMap(); + private final Map<String, Exchange> cacheMap = Maps.newHashMap(); private Persistit persistit; private Volume volume; public Caches(CachesManager caches) { persistit = caches.persistit(); - start(); + doStart(); } @Override public void start() { + // done in constructor + } + + private void doStart() { try { persistit.flush(); volume = persistit.createTemporaryVolume(); @@ -63,12 +67,12 @@ public class Caches implements Startable { public <V> Cache<V> createCache(String cacheName) { Preconditions.checkState(volume != null && volume.isOpened(), "Caches are not initialized"); - Preconditions.checkState(!caches.containsKey(cacheName), "Cache is already created: " + cacheName); + Preconditions.checkState(!cacheMap.containsKey(cacheName), "Cache is already created: " + cacheName); try { Exchange exchange = persistit.getExchange(volume, cacheName, true); exchange.setMaximumValueSize(Value.MAXIMUM_SIZE); Cache<V> cache = new Cache<>(cacheName, exchange); - caches.put(cacheName, exchange); + cacheMap.put(cacheName, exchange); return cache; } catch (Exception e) { throw new IllegalStateException("Fail to create cache: " + cacheName, e); @@ -77,11 +81,11 @@ public class Caches implements Startable { @Override public void stop() { - for (Entry<String, Exchange> e : caches.entrySet()) { + for (Entry<String, Exchange> e : cacheMap.entrySet()) { persistit.releaseExchange(e.getValue()); } - caches.clear(); + cacheMap.clear(); if (volume != null) { try { diff --git a/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java b/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java index 8e38c2031fe..ab769822948 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java +++ b/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java @@ -19,6 +19,8 @@ */ package org.sonar.batch.mediumtest; +import org.sonar.home.log.LogListener; + import com.google.common.base.Function; import com.google.common.io.Files; import org.sonar.api.CoreProperties; @@ -78,10 +80,16 @@ public class BatchMediumTester { private final FakeServerIssuesLoader serverIssues = new FakeServerIssuesLoader(); private final FakeServerLineHashesLoader serverLineHashes = new FakeServerLineHashesLoader(); private final Map<String, String> bootstrapProperties = new HashMap<>(); + private LogListener logListener = null; public BatchMediumTester build() { return new BatchMediumTester(this); } + + public BatchMediumTesterBuilder setLogListener(LogListener listener) { + this.logListener = listener; + return this; + } public BatchMediumTesterBuilder registerPlugin(String pluginKey, File location) { pluginInstaller.add(pluginKey, location); @@ -167,6 +175,7 @@ public class BatchMediumTester { builder.serverLineHashes, new DefaultDebtModel()) .setBootstrapProperties(builder.bootstrapProperties) + .setLogListener(builder.logListener) .build(); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java index 623fd0fa90e..1c3e864170d 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java @@ -21,8 +21,6 @@ package org.sonar.batch.scan; import org.sonar.batch.bootstrap.ProjectTempFolderProvider; -import org.sonar.api.utils.internal.TempFolderCleaner; -import org.sonar.batch.bootstrap.TempFolderProvider; import com.google.common.annotations.VisibleForTesting; import org.sonar.api.CoreProperties; import org.sonar.api.batch.InstantiationStrategy; @@ -155,9 +153,9 @@ public class ProjectScanContainer extends ComponentContainer { Caches.class, BatchComponentCache.class, - //temp + // temp new ProjectTempFolderProvider(), - + // file system InputPathCache.class, PathResolver.class, diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/LifecycleProviderAdapterTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/LifecycleProviderAdapterTest.java new file mode 100644 index 00000000000..5c3bb85a109 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/LifecycleProviderAdapterTest.java @@ -0,0 +1,79 @@ +/* + * 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 org.junit.Before; +import org.picocontainer.Startable; +import org.junit.Test; + +public class LifecycleProviderAdapterTest { + private DummyProvider provider; + + @Before + public void setUp() { + provider = new DummyProvider(); + provider.provide(); + } + + @Test + public void testStart() { + // ComponentLifecycle's start gets called on the provider + provider.start(null); + assertThat(provider.inst.started).isEqualTo(true); + assertThat(provider.isStarted()).isEqualTo(true); + assertThat(provider.inst.stopped).isEqualTo(false); + } + + @Test + public void testSop() { + // ComponentLifecycle's stop gets called on the provider + provider.stop(null); + assertThat(provider.inst.stopped).isEqualTo(true); + assertThat(provider.isStarted()).isEqualTo(false); + assertThat(provider.inst.started).isEqualTo(false); + } + + public class DummyProvided implements Startable { + boolean started = false; + boolean stopped = false; + + @Override + public void start() { + started = true; + } + + @Override + public void stop() { + stopped = true; + } + } + + public class DummyProvider extends LifecycleProviderAdapter { + DummyProvided inst; + + public DummyProvided provide() { + inst = new DummyProvided(); + super.instance = inst; + return inst; + } + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/PersistentCacheProviderTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/PersistentCacheProviderTest.java index 5b6db572df5..24bdf6047d5 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/PersistentCacheProviderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/PersistentCacheProviderTest.java @@ -19,24 +19,20 @@ */ package org.sonar.batch.bootstrap; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.junit.Before; - -import static org.mockito.Mockito.when; +import java.util.Collections; +import org.junit.Before; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Test; public class PersistentCacheProviderTest { private PersistentCacheProvider provider = null; - @Mock private BootstrapProperties props = null; @Before public void prepare() { - MockitoAnnotations.initMocks(this); + props = new BootstrapProperties(Collections.<String, String>emptyMap()); provider = new PersistentCacheProvider(); } @@ -55,7 +51,7 @@ public class PersistentCacheProviderTest { // normally force update (cache disabled) assertThat(provider.provide(props).isForceUpdate()).isTrue(); - when(props.property("sonar.enableHttpCache")).thenReturn("true"); + props.properties().put("sonar.enableHttpCache", "true"); provider = new PersistentCacheProvider(); assertThat(provider.provide(props).isForceUpdate()).isFalse(); } diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ProjectTempFolderProviderTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ProjectTempFolderProviderTest.java index e79f0516159..2b7a0435240 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ProjectTempFolderProviderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ProjectTempFolderProviderTest.java @@ -19,9 +19,9 @@ */ package org.sonar.batch.bootstrap; -import org.apache.commons.io.FileUtils; +import org.sonar.api.utils.TempFolder; -import org.sonar.api.utils.ProjectTempFolder; +import org.apache.commons.io.FileUtils; import com.google.common.collect.ImmutableMap; import org.junit.Rule; import org.junit.Test; @@ -46,7 +46,7 @@ public class ProjectTempFolderProviderTest { File workingDir = temp.newFolder(); File tmpDir = new File(workingDir, ProjectTempFolderProvider.TMP_NAME); - ProjectTempFolder tempFolder = tempFolderProvider.provide(new BootstrapProperties(ImmutableMap.of(CoreProperties.WORKING_DIRECTORY, workingDir.getAbsolutePath()))); + TempFolder tempFolder = tempFolderProvider.provide(new BootstrapProperties(ImmutableMap.of(CoreProperties.WORKING_DIRECTORY, workingDir.getAbsolutePath()))); tempFolder.newDir(); tempFolder.newFile(); assertThat(tmpDir).exists(); @@ -58,7 +58,7 @@ public class ProjectTempFolderProviderTest { File defaultDir = new File(CoreProperties.WORKING_DIRECTORY_DEFAULT_VALUE, ProjectTempFolderProvider.TMP_NAME); try { - ProjectTempFolder tempFolder = tempFolderProvider.provide(new BootstrapProperties(Collections.<String, String>emptyMap())); + TempFolder tempFolder = tempFolderProvider.provide(new BootstrapProperties(Collections.<String, String>emptyMap())); tempFolder.newDir(); tempFolder.newFile(); assertThat(defaultDir).exists(); diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/TempFolderProviderTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/TempFolderProviderTest.java index ce64074d9a6..8c1e23b7b56 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/TempFolderProviderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/TempFolderProviderTest.java @@ -25,7 +25,12 @@ import com.google.common.collect.ImmutableMap; import org.sonar.api.CoreProperties; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.attribute.BasicFileAttributeView; +import java.nio.file.attribute.FileTime; import java.util.Collections; +import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Rule; @@ -40,42 +45,72 @@ public class TempFolderProviderTest { @Test public void createTempFolderProps() throws Exception { - File workingDir = temp.newFolder(); + File workingDir = temp.getRoot(); TempFolder tempFolder = tempFolderProvider.provide(new BootstrapProperties(ImmutableMap.of(CoreProperties.GLOBAL_WORKING_DIRECTORY, workingDir.getAbsolutePath()))); tempFolder.newDir(); tempFolder.newFile(); - assertThat(new File(workingDir, TempFolderProvider.TMP_NAME)).exists(); - assertThat(new File(workingDir, ".sonartmp").list()).hasSize(2); + assertThat(getCreatedTempDir(workingDir)).exists(); + assertThat(getCreatedTempDir(workingDir).list()).hasSize(2); + } + + @Test + public void cleanUpOld() throws IOException { + long creationTime = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(100); + File workingDir = temp.getRoot(); + + for (int i = 0; i < 3; i++) { + File tmp = new File(workingDir, ".sonartmp_" + i); + tmp.mkdirs(); + setFileCreationDate(tmp, creationTime); + } + + tempFolderProvider.provide(new BootstrapProperties(ImmutableMap.of(CoreProperties.GLOBAL_WORKING_DIRECTORY, workingDir.getAbsolutePath()))); + // this also checks that all other temps were deleted + assertThat(getCreatedTempDir(workingDir)).exists(); } @Test public void createTempFolderSonarHome() throws Exception { // with sonar home, it will be in {sonar.home}/.sonartmp - File sonarHome = temp.newFolder(); - File tmpDir = new File(new File(sonarHome, CoreProperties.GLOBAL_WORKING_DIRECTORY_DEFAULT_VALUE), TempFolderProvider.TMP_NAME); + File sonarHome = temp.getRoot(); + File workingDir = new File(sonarHome, CoreProperties.GLOBAL_WORKING_DIRECTORY_DEFAULT_VALUE); TempFolder tempFolder = tempFolderProvider.provide(new BootstrapProperties(ImmutableMap.of("sonar.userHome", sonarHome.getAbsolutePath()))); tempFolder.newDir(); tempFolder.newFile(); - assertThat(tmpDir).exists(); - assertThat(tmpDir.list()).hasSize(2); + assertThat(getCreatedTempDir(workingDir)).exists(); + assertThat(getCreatedTempDir(workingDir).list()).hasSize(2); } @Test public void createTempFolderDefault() throws Exception { + File userHome = temp.getRoot(); + System.setProperty("user.home", userHome.getAbsolutePath()); + // if nothing is defined, it will be in {user.home}/.sonar/.sonartmp File defaultSonarHome = new File(System.getProperty("user.home"), ".sonar"); - File tmpDir = new File(new File(defaultSonarHome, CoreProperties.GLOBAL_WORKING_DIRECTORY_DEFAULT_VALUE), TempFolderProvider.TMP_NAME); + File workingDir = new File(defaultSonarHome, CoreProperties.GLOBAL_WORKING_DIRECTORY_DEFAULT_VALUE).getAbsoluteFile(); try { TempFolder tempFolder = tempFolderProvider.provide(new BootstrapProperties(Collections.<String, String>emptyMap())); tempFolder.newDir(); tempFolder.newFile(); - assertThat(tmpDir).exists(); - assertThat(tmpDir.list()).hasSize(2); + assertThat(getCreatedTempDir(workingDir)).exists(); + assertThat(getCreatedTempDir(workingDir).list()).hasSize(2); } finally { - FileUtils.deleteDirectory(tmpDir); + FileUtils.deleteDirectory(getCreatedTempDir(workingDir)); } } + + private File getCreatedTempDir(File workingDir) { + assertThat(workingDir.listFiles()).hasSize(1); + return workingDir.listFiles()[0]; + } + + private void setFileCreationDate(File f, long time) throws IOException { + BasicFileAttributeView attributes = Files.getFileAttributeView(f.toPath(), BasicFileAttributeView.class); + FileTime creationTime = FileTime.fromMillis(time); + attributes.setTimes(creationTime, creationTime, creationTime); + } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrapper/LogCallbackAppenderTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrapper/LogCallbackAppenderTest.java new file mode 100644 index 00000000000..ea1fd3a470e --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrapper/LogCallbackAppenderTest.java @@ -0,0 +1,66 @@ +/* + * 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.bootstrapper; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.spi.ILoggingEvent; +import org.junit.Test; +import org.sonar.home.log.LogListener; +import org.junit.Before; + +public class LogCallbackAppenderTest { + private LogListener listener; + private LogCallbackAppender appender; + private ILoggingEvent event; + + @Before + public void setUp() { + listener = mock(LogListener.class); + appender = new LogCallbackAppender(listener); + event = mock(ILoggingEvent.class); + when(event.getMessage()).thenReturn("test"); + when(event.getLevel()).thenReturn(Level.INFO); + } + + @Test + public void testAppendLog() { + + appender.append(event); + + verify(event).getMessage(); + verify(event).getLevel(); + + verify(listener).log("test", LogListener.Level.INFO); + + verifyNoMoreInteractions(event, listener); + } + + @Test + public void testChangeTarget() { + listener = mock(LogListener.class); + appender.setTarget(listener); + testAppendLog(); + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrapper/PrintStreamTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrapper/PrintStreamTest.java deleted file mode 100644 index da833eae508..00000000000 --- a/sonar-batch/src/test/java/org/sonar/batch/bootstrapper/PrintStreamTest.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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.bootstrapper; - -import ch.qos.logback.core.encoder.EchoEncoder; -import ch.qos.logback.classic.spi.ILoggingEvent; -import org.mockito.Matchers; -import ch.qos.logback.core.encoder.Encoder; -import ch.qos.logback.core.Context; -import org.junit.Test; -import org.junit.Before; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.io.PrintStream; - -import static org.assertj.core.api.Assertions.assertThat; - -import static org.mockito.Mockito.when; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.mock; - -public class PrintStreamTest { - private static final String TEST_STR = "foo"; - - private ByteArrayOutputStream os; - private PrintStream stream; - private PrintStreamAppender<ILoggingEvent> appender; - private Context context = mock(Context.class); - - private Encoder<ILoggingEvent> encoder = mock(Encoder.class); - private ILoggingEvent event = mock(ILoggingEvent.class); - - @Before - public void setUp() { - os = new ByteArrayOutputStream(); - stream = new PrintStream(os); - - appender = new PrintStreamAppender<ILoggingEvent>(stream); - when(event.getMessage()).thenReturn(TEST_STR); - when(event.toString()).thenReturn(TEST_STR); - } - - @Test - public void testNullStream() { - appender.setContext(mock(Context.class)); - appender.setEncoder(encoder); - appender.setTarget(null); - appender.start(); - appender.doAppend(event); - - verifyNoMoreInteractions(encoder); - } - - @Test - public void testEncoder() throws IOException { - appender.setContext(mock(Context.class)); - appender.setEncoder(encoder); - appender.start(); - appender.doAppend(event); - - verify(encoder, times(1)).init(Matchers.notNull(OutputStream.class)); - verify(encoder, times(1)).doEncode(event); - - } - - @Test - public void testWrite() { - encoder = new EchoEncoder<>(); - encoder.setContext(context); - encoder.start(); - - appender.setContext(mock(Context.class)); - appender.setEncoder(encoder); - appender.setTarget(stream); - appender.start(); - - appender.doAppend(event); - - assertThat(os.toString()).isEqualTo(TEST_STR + System.lineSeparator()); - } -} diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/CachesTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/CachesTest.java index 801d0f3d5ed..411435aebec 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/index/CachesTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/index/CachesTest.java @@ -65,8 +65,6 @@ public class CachesTest extends AbstractCachesTest { public void leak_test() throws PersistitException { caches.stop(); - System.out.println(cachesManager.tempDir()); - int len = 1 * 1024 * 1024; StringBuilder sb = new StringBuilder(len); for (int i = 0; i < len; i++) { diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/log/LogListenerTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/log/LogListenerTest.java new file mode 100644 index 00000000000..c50f9e20adf --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/log/LogListenerTest.java @@ -0,0 +1,187 @@ +/* + * 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.mediumtest.log; + +import org.junit.BeforeClass; +import org.junit.AfterClass; +import org.sonar.home.log.LogListener; +import org.sonar.home.log.LogListener.Level; +import org.apache.commons.io.FileUtils; +import org.junit.Test; +import com.google.common.collect.ImmutableMap; +import org.junit.After; +import org.junit.Before; +import org.sonar.batch.mediumtest.BatchMediumTester; +import org.sonar.xoo.XooPlugin; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; +import java.util.LinkedList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Rule; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; + +public class LogListenerTest { + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private Pattern simpleTimePattern = Pattern.compile("\\d{2}:\\d{2}:\\d{2}"); + private List<LogEvent> logOutput; + private ByteArrayOutputStream stdOutTarget = new ByteArrayOutputStream(); + private ByteArrayOutputStream stdErrTarget = new ByteArrayOutputStream(); + private static PrintStream savedStdOut; + private static PrintStream savedStdErr; + + public BatchMediumTester tester = BatchMediumTester.builder() + .registerPlugin("xoo", new XooPlugin()) + .addDefaultQProfile("xoo", "Sonar Way") + .setLogListener(new SimpleLogListener()) + .build(); + + private File baseDir; + + private ImmutableMap.Builder<String, String> builder; + + @BeforeClass + public static void backupStdStreams() { + savedStdOut = System.out; + savedStdErr = System.err; + } + + @AfterClass + public static void resumeStdStreams() { + if (savedStdOut != null) { + System.setOut(savedStdOut); + } + if (savedStdErr != null) { + System.setErr(savedStdErr); + } + } + + @Before + public void prepare() throws IOException { + System.setOut(new PrintStream(stdOutTarget)); + System.setErr(new PrintStream(stdErrTarget)); + logOutput = new LinkedList<>(); + tester.start(); + + baseDir = temp.newFolder(); + + builder = ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project"); + } + + private void assertNoStdOutput() { + assertThat(stdOutTarget.toByteArray()).isEmpty(); + assertThat(stdErrTarget.toByteArray()).isEmpty(); + } + + /** + * Check that log message is not formatted, i.e. has no log level and timestamp. + */ + private void assertMsgClean(String msg) { + for (Level l : Level.values()) { + assertThat(msg).doesNotContain(l.toString()); + } + + Matcher matcher = simpleTimePattern.matcher(msg); + assertThat(matcher.find()).isFalse(); + } + + @After + public void stop() { + tester.stop(); + } + + @Test + public void testNoStdLog() throws IOException { + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + File xooFile = new File(srcDir, "sample.xoo"); + FileUtils.write(xooFile, "Sample xoo\ncontent"); + + tester.newTask() + .properties(builder + .put("sonar.sources", "src") + .build()) + .start(); + + assertNoStdOutput(); + assertThat(logOutput).isNotEmpty(); + for (LogEvent e : logOutput) { + savedStdOut.println("[captured]" + e.level + " " + e.msg); + } + } + + @Test + public void testNoFormattedMsgs() throws IOException { + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + File xooFile = new File(srcDir, "sample.xoo"); + FileUtils.write(xooFile, "Sample xoo\ncontent"); + + tester.newTask() + .properties(builder + .put("sonar.sources", "src") + .build()) + .start(); + + assertNoStdOutput(); + + for (LogEvent e : logOutput) { + assertMsgClean(e.msg); + savedStdOut.println("[captured]" + e.level + " " + e.msg); + } + } + + private class SimpleLogListener implements LogListener { + @Override + public void log(String msg, Level level) { + logOutput.add(new LogEvent(msg, level)); + } + } + + private static class LogEvent { + String msg; + Level level; + + LogEvent(String msg, Level level) { + this.msg = msg; + this.level = level; + } + } +} |