Fix logging when creating cache file

This commit is contained in:
Clément Fournier
2018-10-22 19:22:19 +02:00
parent d3880ed349
commit 583c56bd69
3 changed files with 64 additions and 27 deletions

View File

@ -43,11 +43,11 @@ public abstract class AbstractAnalysisCache implements AnalysisCache {
protected final String pmdVersion;
protected final ConcurrentMap<String, AnalysisResult> fileResultsCache;
protected final ConcurrentMap<String, AnalysisResult> updatedResultsCache;
protected final CachedRuleMapper ruleMapper = new CachedRuleMapper();
protected long rulesetChecksum;
protected long auxClassPathChecksum;
protected long executionClassPathChecksum;
protected final CachedRuleMapper ruleMapper = new CachedRuleMapper();
/**
* Creates a new empty cache
*/
@ -62,10 +62,10 @@ public abstract class AbstractAnalysisCache implements AnalysisCache {
// There is a new file being analyzed, prepare entry in updated cache
final AnalysisResult updatedResult = new AnalysisResult(sourceFile);
updatedResultsCache.put(sourceFile.getPath(), updatedResult);
// Now check the old cache
final AnalysisResult analysisResult = fileResultsCache.get(sourceFile.getPath());
// is this a known file? has it changed?
final boolean result = analysisResult != null
&& analysisResult.getFileChecksum() == updatedResult.getFileChecksum();
@ -99,11 +99,19 @@ public abstract class AbstractAnalysisCache implements AnalysisCache {
updatedResultsCache.remove(sourceFile.getPath());
}
/**
* Returns true if the cache exists. If so, normal cache validity checks
* will be performed. Otherwise, the cache is necessarily invalid (e.g. on a first run).
*/
protected abstract boolean cacheExists();
@Override
public void checkValidity(final RuleSets ruleSets, final ClassLoader auxclassPathClassLoader) {
boolean cacheIsValid = true;
boolean cacheIsValid = cacheExists();
if (ruleSets.getChecksum() != rulesetChecksum) {
if (cacheIsValid && ruleSets.getChecksum() != rulesetChecksum) {
LOG.info("Analysis cache invalidated, rulesets changed.");
cacheIsValid = false;
}
@ -112,7 +120,7 @@ public abstract class AbstractAnalysisCache implements AnalysisCache {
if (auxclassPathClassLoader instanceof URLClassLoader) {
final URLClassLoader urlClassLoader = (URLClassLoader) auxclassPathClassLoader;
currentAuxClassPathChecksum = computeClassPathHash(urlClassLoader.getURLs());
if (cacheIsValid && currentAuxClassPathChecksum != auxClassPathChecksum) {
// Do we even care?
for (final Rule r : ruleSets.getAllRules()) {
@ -126,9 +134,9 @@ public abstract class AbstractAnalysisCache implements AnalysisCache {
} else {
currentAuxClassPathChecksum = 0;
}
final long currentExecutionClassPathChecksum = computeClassPathHash(getClassPathEntries());
if (currentExecutionClassPathChecksum != executionClassPathChecksum) {
if (cacheIsValid && currentExecutionClassPathChecksum != executionClassPathChecksum) {
LOG.info("Analysis cache invalidated, execution classpath changed.");
cacheIsValid = false;
}
@ -149,7 +157,7 @@ public abstract class AbstractAnalysisCache implements AnalysisCache {
final String classpath = System.getProperty("java.class.path");
final String[] classpathEntries = classpath.split(File.pathSeparator);
final List<URL> entries = new ArrayList<>();
try {
for (final String entry : classpathEntries) {
final File f = new File(entry);
@ -173,7 +181,7 @@ public abstract class AbstractAnalysisCache implements AnalysisCache {
LOG.log(Level.SEVERE, "Incremental analysis can't check execution classpath contents", e);
throw new RuntimeException(e);
}
return entries.toArray(new URL[0]);
}
@ -199,7 +207,7 @@ public abstract class AbstractAnalysisCache implements AnalysisCache {
@Override
public void ruleViolationAdded(final RuleViolation ruleViolation) {
final AnalysisResult analysisResult = updatedResultsCache.get(ruleViolation.getFilename());
analysisResult.addViolation(ruleViolation);
}

View File

@ -13,16 +13,22 @@ import net.sourceforge.pmd.ThreadSafeReportListener;
/**
* An analysis cache for incremental analysis.
* Simultaneously manages the old version of the cache,
* and the new, most up-to-date violation cache.
*/
public interface AnalysisCache extends ThreadSafeReportListener {
/**
* Persist the analysis results on whatever means is used by the cache
* Persists the updated analysis results on whatever medium is used by the cache.
*/
void persist();
/**
* Check if a given file is up to date in the cache and can be skipped from analysis
* Checks if a given file is up to date in the cache and can be skipped from analysis.
* Regardless of the return value of this method, each call adds the parameter to the
* updated cache, which allows {@link #ruleViolationAdded(RuleViolation)} to add a rule
* violation to the file. TODO is this really best behaviour? This side-effects seems counter-intuitive.
*
* @param sourceFile The file to check in the cache
* @return True if the cache is a hit, false otherwise
*/
@ -36,13 +42,17 @@ public interface AnalysisCache extends ThreadSafeReportListener {
List<RuleViolation> getCachedViolations(File sourceFile);
/**
* Notifies the cache that analysis of the given file has failed and should not be cached
* Notifies the cache that analysis of the given file has failed and should not be cached.
* @param sourceFile The file whose analysis failed
*/
void analysisFailed(File sourceFile);
/**
* Checks if the cache is valid for the configured rulesets and class loader.
* If the provided rulesets and classpath don't match those of the cache, the
* cache is invalidated. This needs to be called before analysis, as it
* conditions the good behaviour of {@link #isUpToDate(File)}.
*
* @param ruleSets The rulesets configured for this analysis.
* @param auxclassPathClassLoader The class loader for auxclasspath configured for this analysis.
*/

View File

@ -26,7 +26,7 @@ import net.sourceforge.pmd.RuleViolation;
public class FileAnalysisCache extends AbstractAnalysisCache {
private final File cacheFile;
/**
* Creates a new cache backed by the given file, and attempts to load pre-existing data from it.
* @param cache The file on which to store analysis cache
@ -43,26 +43,26 @@ public class FileAnalysisCache extends AbstractAnalysisCache {
* @param cacheFile The file which backs the file analysis cache.
*/
private void loadFromFile(final File cacheFile) {
if (cacheFile.exists()) {
if (cacheExists()) {
try (
DataInputStream inputStream = new DataInputStream(
new BufferedInputStream(new FileInputStream(cacheFile)));
) {
final String cacheVersion = inputStream.readUTF();
if (PMDVersion.VERSION.equals(cacheVersion)) {
// Cache seems valid, load the rest
// Get checksums
rulesetChecksum = inputStream.readLong();
auxClassPathChecksum = inputStream.readLong();
executionClassPathChecksum = inputStream.readLong();
// Cached results
while (inputStream.available() > 0) {
final String fileName = inputStream.readUTF();
final long checksum = inputStream.readLong();
final int countViolations = inputStream.readInt();
final List<RuleViolation> violations = new ArrayList<>(countViolations);
for (int i = 0; i < countViolations; i++) {
@ -81,13 +81,23 @@ public class FileAnalysisCache extends AbstractAnalysisCache {
} catch (final IOException e) {
LOG.severe("Could not load analysis cache from file. " + e.getMessage());
}
} else if (cacheFile.isDirectory()) {
LOG.severe("The configured cache location must be the path to a file, but is a directory.");
}
}
@Override
public void persist() {
if (cacheFile.isDirectory()) {
LOG.severe("Cannot persist the cache, the given path points to a directory.");
return;
}
boolean cacheFileShouldBeCreated = !cacheFile.exists();
// Create directories missing along the way
if (!cacheFile.exists()) {
if (cacheFileShouldBeCreated) {
final File parentFile = cacheFile.getAbsoluteFile().getParentFile();
if (parentFile != null && !parentFile.exists()) {
parentFile.mkdirs();
@ -99,26 +109,35 @@ public class FileAnalysisCache extends AbstractAnalysisCache {
new BufferedOutputStream(new FileOutputStream(cacheFile)))
) {
outputStream.writeUTF(pmdVersion);
outputStream.writeLong(rulesetChecksum);
outputStream.writeLong(auxClassPathChecksum);
outputStream.writeLong(executionClassPathChecksum);
for (final Map.Entry<String, AnalysisResult> resultEntry : updatedResultsCache.entrySet()) {
final List<RuleViolation> violations = resultEntry.getValue().getViolations();
outputStream.writeUTF(resultEntry.getKey());
outputStream.writeLong(resultEntry.getValue().getFileChecksum());
outputStream.writeInt(violations.size());
for (final RuleViolation rv : violations) {
CachedRuleViolation.storeToStream(outputStream, rv);
}
}
LOG.info("Analysis cache updated");
if (cacheFileShouldBeCreated) {
LOG.info("Analysis cache created");
} else {
LOG.info("Analysis cache updated");
}
} catch (final IOException e) {
LOG.severe("Could not persist analysis cache to file. " + e.getMessage());
}
}
@Override
protected boolean cacheExists() {
return cacheFile.exists() && cacheFile.isFile() && cacheFile.length() > 0;
}
}