Browse Source

SONAR-6649 Move initialization of persistit caches to global context

tags/5.2-RC1
Duarte Meneses 9 years ago
parent
commit
d2b1467062
25 changed files with 627 additions and 194 deletions
  1. 2
    2
      pom.xml
  2. 1
    1
      sonar-batch/pom.xml
  3. 7
    6
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java
  4. 85
    0
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/LifecycleProviderAdapter.java
  5. 14
    7
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/PersistentCacheProvider.java
  6. 12
    13
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectTempFolderProvider.java
  7. 57
    5
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/TempFolderProvider.java
  8. 4
    4
      sonar-batch/src/main/java/org/sonar/batch/bootstrapper/LogCallbackAppender.java
  9. 10
    6
      sonar-batch/src/main/java/org/sonar/batch/index/Caches.java
  10. 9
    0
      sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java
  11. 2
    4
      sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
  12. 79
    0
      sonar-batch/src/test/java/org/sonar/batch/bootstrap/LifecycleProviderAdapterTest.java
  13. 4
    8
      sonar-batch/src/test/java/org/sonar/batch/bootstrap/PersistentCacheProviderTest.java
  14. 4
    4
      sonar-batch/src/test/java/org/sonar/batch/bootstrap/ProjectTempFolderProviderTest.java
  15. 46
    11
      sonar-batch/src/test/java/org/sonar/batch/bootstrap/TempFolderProviderTest.java
  16. 66
    0
      sonar-batch/src/test/java/org/sonar/batch/bootstrapper/LogCallbackAppenderTest.java
  17. 0
    102
      sonar-batch/src/test/java/org/sonar/batch/bootstrapper/PrintStreamTest.java
  18. 0
    2
      sonar-batch/src/test/java/org/sonar/batch/index/CachesTest.java
  19. 187
    0
      sonar-batch/src/test/java/org/sonar/batch/mediumtest/log/LogListenerTest.java
  20. 6
    2
      sonar-home/src/main/java/org/sonar/home/cache/PersistentCache.java
  21. 5
    3
      sonar-home/src/main/java/org/sonar/home/cache/PersistentCacheBuilder.java
  22. 21
    3
      sonar-home/src/test/java/org/sonar/home/cache/PersistentCacheTest.java
  23. 5
    6
      sonar-plugin-api/src/main/java/org/sonar/api/utils/internal/DefaultTempFolder.java
  24. 1
    3
      sonar-plugin-api/src/main/java/org/sonar/api/utils/internal/JUnitTempFolder.java
  25. 0
    2
      sonar-plugin-api/src/main/java/org/sonar/api/utils/internal/TempFolderCleaner.java

+ 2
- 2
pom.xml View File

@@ -1091,9 +1091,9 @@
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>org.codehaus.sonar</groupId>
<groupId>org.sonarsource</groupId>
<artifactId>sonar-persistit</artifactId>
<version>3.3.2-SNAPSHOT</version>
<version>3.3.2</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>

+ 1
- 1
sonar-batch/pom.xml View File

@@ -25,7 +25,7 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.sonar</groupId>
<groupId>org.sonarsource</groupId>
<artifactId>sonar-persistit</artifactId>
</dependency>
<dependency>

+ 7
- 6
sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java View File

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

}

+ 85
- 0
sonar-batch/src/main/java/org/sonar/batch/bootstrap/LifecycleProviderAdapter.java View File

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

}

+ 14
- 7
sonar-batch/src/main/java/org/sonar/batch/bootstrap/PersistentCacheProvider.java View File

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

+ 12
- 13
sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectTempFolderProvider.java View File

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

}

+ 57
- 5
sonar-batch/src/main/java/org/sonar/batch/bootstrap/TempFolderProvider.java View File

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

+ 4
- 4
sonar-batch/src/main/java/org/sonar/batch/bootstrapper/LogCallbackAppender.java View File

@@ -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:

+ 10
- 6
sonar-batch/src/main/java/org/sonar/batch/index/Caches.java View File

@@ -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 {

+ 9
- 0
sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java View File

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


+ 2
- 4
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java View File

@@ -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,

+ 79
- 0
sonar-batch/src/test/java/org/sonar/batch/bootstrap/LifecycleProviderAdapterTest.java View File

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

+ 4
- 8
sonar-batch/src/test/java/org/sonar/batch/bootstrap/PersistentCacheProviderTest.java View File

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

+ 4
- 4
sonar-batch/src/test/java/org/sonar/batch/bootstrap/ProjectTempFolderProviderTest.java View File

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

+ 46
- 11
sonar-batch/src/test/java/org/sonar/batch/bootstrap/TempFolderProviderTest.java View File

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

+ 66
- 0
sonar-batch/src/test/java/org/sonar/batch/bootstrapper/LogCallbackAppenderTest.java View File

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

+ 0
- 102
sonar-batch/src/test/java/org/sonar/batch/bootstrapper/PrintStreamTest.java View File

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

+ 0
- 2
sonar-batch/src/test/java/org/sonar/batch/index/CachesTest.java View File

@@ -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++) {

+ 187
- 0
sonar-batch/src/test/java/org/sonar/batch/mediumtest/log/LogListenerTest.java View File

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

+ 6
- 2
sonar-home/src/main/java/org/sonar/home/cache/PersistentCache.java View File

@@ -52,15 +52,19 @@ public class PersistentCache {
// eviction strategy is to expire entries after modification once a time duration has elapsed
private final long defaultDurationToExpireMs;
private final Log log;
private final boolean forceUpdate;
private boolean forceUpdate;

public PersistentCache(Path baseDir, long defaultDurationToExpireMs, Log log, boolean forceUpdate) {
this.baseDir = baseDir;
this.defaultDurationToExpireMs = defaultDurationToExpireMs;
this.log = log;
this.forceUpdate = forceUpdate;

reconfigure(forceUpdate);
log.info("cache: " + baseDir + ", default expiration time (ms): " + defaultDurationToExpireMs);
}

public void reconfigure(boolean forceUpdate) {
this.forceUpdate = forceUpdate;

if (forceUpdate) {
log.debug("cache: forcing update");

+ 5
- 3
sonar-home/src/main/java/org/sonar/home/cache/PersistentCacheBuilder.java View File

@@ -30,17 +30,19 @@ import java.nio.file.Paths;
import java.util.concurrent.TimeUnit;

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 = null;
private Log log = new StandardLog();
private String name = "ws_cache";

public PersistentCache build() {
if (cachePath == null) {
setSonarHome(findHome());
}

return new PersistentCache(cachePath, TimeUnit.MILLISECONDS.convert(1L, TimeUnit.DAYS), log, forceUpdate);
return new PersistentCache(cachePath, DEFAULT_EXPIRE_DURATION, log, forceUpdate);
}

public PersistentCacheBuilder setLog(Log log) {
@@ -50,7 +52,7 @@ public class PersistentCacheBuilder {

public PersistentCacheBuilder setSonarHome(@Nullable Path p) {
if (p != null) {
this.cachePath = p.resolve(name);
this.cachePath = p.resolve(DIR_NAME);
}
return this;
}

+ 21
- 3
sonar-home/src/test/java/org/sonar/home/cache/PersistentCacheTest.java View File

@@ -19,12 +19,13 @@
*/
package org.sonar.home.cache;

import org.apache.commons.io.FileUtils;

import org.sonar.home.log.Slf4jLog;
import org.junit.Rule;
import org.junit.rules.TemporaryFolder;

import java.io.File;
import java.nio.file.Path;
import java.util.concurrent.Callable;

import static org.mockito.Mockito.when;
@@ -95,9 +96,26 @@ public class PersistentCacheTest {
assertCacheHit(true);
}

@Test
public void testReconfigure() throws Exception {
cache = new PersistentCache(tmp.getRoot().toPath(), Long.MAX_VALUE, log, true);
assertCacheHit(false);
assertCacheHit(false);

File root = tmp.getRoot();
FileUtils.deleteDirectory(root);

// should re-create cache directory and start using the cache
cache.reconfigure(false);
assertThat(root).exists();

assertCacheHit(false);
assertCacheHit(true);
}

@Test
public void testExpiration() throws Exception {
//negative time to make sure it is expired on the second call
// negative time to make sure it is expired on the second call
cache = new PersistentCache(tmp.getRoot().toPath(), -100, log, false);
assertCacheHit(false);
assertCacheHit(false);
@@ -118,7 +136,7 @@ public class PersistentCacheTest {
return VALUE;
}
}
/**
* WSCache should be transparent regarding exceptions: if an exception is thrown by the value loader, it should pass through
* the cache to the original caller using the cache.

+ 5
- 6
sonar-plugin-api/src/main/java/org/sonar/api/utils/internal/DefaultTempFolder.java View File

@@ -19,7 +19,6 @@
*/
package org.sonar.api.utils.internal;

import org.sonar.api.utils.ProjectTempFolder;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.utils.TempFolder;
@@ -30,21 +29,21 @@ import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;

public class DefaultTempFolder implements TempFolder, ProjectTempFolder {
public class DefaultTempFolder implements TempFolder {

/** Maximum loop count when creating temp directories. */
private static final int TEMP_DIR_ATTEMPTS = 10000;

private final File tempDir;
private final boolean cleanUp;
private final boolean deleteOnExit;

public DefaultTempFolder(File tempDir) {
this(tempDir, false);
}

public DefaultTempFolder(File tempDir, boolean cleanUp) {
public DefaultTempFolder(File tempDir, boolean deleteOnExit) {
this.tempDir = tempDir;
this.cleanUp = cleanUp;
this.deleteOnExit = deleteOnExit;
}

@Override
@@ -114,7 +113,7 @@ public class DefaultTempFolder implements TempFolder, ProjectTempFolder {
}

public void stop() {
if(cleanUp) {
if (deleteOnExit) {
clean();
}
}

+ 1
- 3
sonar-plugin-api/src/main/java/org/sonar/api/utils/internal/JUnitTempFolder.java View File

@@ -19,8 +19,6 @@
*/
package org.sonar.api.utils.internal;

import org.sonar.api.utils.ProjectTempFolder;

import org.apache.commons.lang.StringUtils;
import org.junit.rules.ExternalResource;
import org.junit.rules.TemporaryFolder;
@@ -53,7 +51,7 @@ import java.io.IOException;
*
* @since 5.1
*/
public class JUnitTempFolder extends ExternalResource implements TempFolder, ProjectTempFolder {
public class JUnitTempFolder extends ExternalResource implements TempFolder {

private final TemporaryFolder junit = new TemporaryFolder();


+ 0
- 2
sonar-plugin-api/src/main/java/org/sonar/api/utils/internal/TempFolderCleaner.java View File

@@ -19,11 +19,9 @@
*/
package org.sonar.api.utils.internal;

import org.sonar.api.batch.BatchSide;
import org.sonar.api.server.ServerSide;
import org.sonar.api.utils.TempFolder;

@BatchSide
@ServerSide
public class TempFolderCleaner {


Loading…
Cancel
Save