io.cloudslang.lang.enforcer.SpringCleansingRule Maven / Gradle / Ivy
/*******************************************************************************
* (c) Copyright 2016 Hewlett-Packard Development Company, L.P.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Apache License v2.0 which accompany this distribution.
*
* The Apache License is available at
* http://www.apache.org/licenses/LICENSE-2.0
*
*******************************************************************************/
package io.cloudslang.lang.enforcer;
import org.apache.maven.enforcer.rule.api.EnforcerRule;
import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static java.lang.String.format;
import static java.lang.String.valueOf;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.file.Files.isRegularFile;
import static java.nio.file.LinkOption.NOFOLLOW_LINKS;
import static org.apache.commons.io.FileUtils.iterateFiles;
import static org.apache.commons.io.FileUtils.readFileToString;
import static org.apache.commons.lang3.StringUtils.countMatches;
public class SpringCleansingRule implements EnforcerRule {
private static final String JAVA = "java";
private static final String OPTIONAL_SPACER = "(\\s)*";
private static final String REGEX_DOT = "\\.";
private static final String OPTIONAL_STATIC = "(static )?";
private static final String ORG = "org";
private static final String SPRINGFRAMEWORK = "springframework";
private static final String SPACER_DOT_SPACER = OPTIONAL_SPACER + REGEX_DOT + OPTIONAL_SPACER;
private static final String regexImport = "import " + OPTIONAL_STATIC + OPTIONAL_SPACER + ORG +
SPACER_DOT_SPACER + SPRINGFRAMEWORK + "[^;]+" + ";" ;
private static final Pattern patternImport = Pattern.compile(regexImport);
private static final String regexCodeLine = OPTIONAL_SPACER + ORG + OPTIONAL_SPACER + REGEX_DOT +
OPTIONAL_SPACER + SPRINGFRAMEWORK + "(.|\\s)+;" ;
private static final Pattern patternCodeLine = Pattern.compile(regexCodeLine);
private static final String STARTED_SCANNING_ORG_SPRINGFRAMEWORK = "Scanning file for org.springframework " +
"in file '%s'";
private static final String FINISHED_SCANNING_ORG_SPRINGFRAMEWORK = "Finished scanning for org.springframework " +
"in file '%s'";
private static final String FOUND_USAGE_OF_ORG_SPRINGFRAMEWORK_IN_IMPORT_AT_LINE = "Found usage of " +
"org.springframework in import at line ";
private static final String FOUND_USAGE_OF_ORG_SPRINGFRAMEWORK_IN_CODE_FRAGMENT_AT_LINE = "Found usage of " +
"org.springframework in code fragment at line ";
private static final String CONTEXT = "context";
private static final String ANNOTATION = "annotation";
private static final String CONFIGURATION = "Configuration";
private static final String SPRING_CONFIGURATION_CLASS = OPTIONAL_SPACER + ORG + SPACER_DOT_SPACER +
SPRINGFRAMEWORK + SPACER_DOT_SPACER + CONTEXT + SPACER_DOT_SPACER + ANNOTATION + SPACER_DOT_SPACER +
CONFIGURATION;
private static final Pattern patternConfigurationClass = Pattern.compile(SPRING_CONFIGURATION_CLASS);
private static final String FOUND_UNWANTED_OCCURRENCE_OF_ORG_SPRINGFRAMEWORK = "Found unwanted occurrence of " +
"org.springframework at line %d in source '%s'";
private static final String IN_SOURCE = " in source '";
private static final String NEW_LINE = "\n";
/**
* Simple param. This rule will fail if the value is true.
*/
private boolean shouldFail = false;
public void execute(EnforcerRuleHelper helper) throws EnforcerRuleException {
Log log = helper.getLog();
try {
// get the various expressions out of the helper.
MavenProject project = (MavenProject) helper.evaluate("${project}");
// MavenSession session = (MavenSession) helper.evaluate("${session}");
// String target = (String) helper.evaluate("${project.build.directory}");
// String artifactId = (String) helper.evaluate("${project.artifactId}");
// ArtifactResolver resolver = (ArtifactResolver) helper.getComponent(ArtifactResolver.class);
// RuntimeInfo rti = (RuntimeInfo) helper.getComponent(RuntimeInfo.class);
List compileSourceRoots = project.getCompileSourceRoots();
for (Object compileSourceRoot : compileSourceRoots) {
String path = (String) compileSourceRoot;
applyForJavaSourcesInRoot(path, log);
}
} catch (ExpressionEvaluationException e) {
throw new EnforcerRuleException("Unable to lookup an expression " + e.getLocalizedMessage(), e);
}
}
/**
* If your rule is cacheable, you must return a unique id when parameters or conditions
* change that would cause the result to be different. Multiple cached results are stored
* based on their id.
*
* The easiest way to do this is to return a hash computed from the values of your parameters.
*
* If your rule is not cacheable, then the result here is not important, you may return anything.
*/
public String getCacheId() {
//no hash on boolean...only parameter so no hash is needed.
return "" + this.shouldFail;
}
/**
* This tells the system if the results are cacheable at all. Keep in mind that during
* forked builds and other things, a given rule may be executed more than once for the same
* project. This means that even things that change from project to project may still
* be cacheable in certain instances.
*/
public boolean isCacheable() {
return false;
}
/**
* If the rule is cacheable and the same id is found in the cache, the stored results
* are passed to this method to allow double checking of the results. Most of the time
* this can be done by generating unique ids, but sometimes the results of objects returned
* by the helper need to be queried. You may for example, store certain objects in your rule
* and then query them later.
*/
public boolean isResultValid(EnforcerRule arg0) {
return false;
}
private boolean isSpringConfigurationAnnotatedClass(final String contents) {
Matcher matcher = patternConfigurationClass.matcher(contents);
return matcher.find();
}
private void applyForJavaSourcesInRoot(final String path, final Log log) throws EnforcerRuleException {
Iterator fileIterator = iterateFiles(new File(path), new String[]{JAVA}, true);
while (fileIterator.hasNext()) {
File source = fileIterator.next();
if (isRegularFile(source.toPath(), NOFOLLOW_LINKS)) {
if (log.isDebugEnabled()) {
log.debug(format(STARTED_SCANNING_ORG_SPRINGFRAMEWORK, source.getAbsolutePath()));
}
try {
String contents = readFileToString(source, UTF_8.displayName());
if (isSpringConfigurationAnnotatedClass(contents)) {
log.info(format("Skipping verification for Spring configuration class in file '%s'",
source.getAbsolutePath()));
continue;
}
// At this point it is clear this is a regular Java class that is not a Spring Configuration class
// and just validate we don't have org.springframework in it
findMatchesUsingPattern(source, log, contents, patternImport,
FOUND_USAGE_OF_ORG_SPRINGFRAMEWORK_IN_IMPORT_AT_LINE);
findMatchesUsingPattern(source, log, contents, patternCodeLine,
FOUND_USAGE_OF_ORG_SPRINGFRAMEWORK_IN_CODE_FRAGMENT_AT_LINE);
} catch (IOException ignore) {
log.error(format("Could not process file '%s'", source));
}
if (log.isDebugEnabled()) {
log.debug(format(FINISHED_SCANNING_ORG_SPRINGFRAMEWORK, source.getAbsolutePath()));
}
}
}
}
private void findMatchesUsingPattern(final File javaFile, final Log log, final String contents,
final Pattern pattern, String message) throws EnforcerRuleException {
Matcher matcherImportForFile = pattern.matcher(contents);
if (matcherImportForFile.find()) {
int lineNumber = findLineNumber(contents.substring(0, matcherImportForFile.start()));
log.info(format(FOUND_UNWANTED_OCCURRENCE_OF_ORG_SPRINGFRAMEWORK, lineNumber, javaFile.getAbsolutePath()));
if (shouldFail) {
throw new EnforcerRuleException(message + valueOf(lineNumber) + IN_SOURCE +
javaFile.getAbsolutePath() + "'");
}
}
}
private int findLineNumber(String contents) {
return countMatches(contents, NEW_LINE) + 1;
}
}