io.cloudslang.lang.enforcer.SpringCleansingRule.java Source code

Java tutorial

Introduction

Here is the source code for io.cloudslang.lang.enforcer.SpringCleansingRule.java

Source

/*******************************************************************************
 * (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.
     * <p>
     * The easiest way to do this is to return a hash computed from the values of your parameters.
     * <p>
     * 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<File> 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;
    }

}