Java tutorial
package org.apache.maven.plugin.checkstyle.exec; /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import java.io.ByteArrayInputStream; import java.io.Closeable; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import org.apache.commons.io.IOUtils; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.DependencyResolutionRequiredException; import org.apache.maven.model.Resource; import org.apache.maven.project.MavenProject; import org.codehaus.plexus.component.annotations.Component; import org.codehaus.plexus.component.annotations.Requirement; import org.codehaus.plexus.logging.AbstractLogEnabled; import org.codehaus.plexus.resource.ResourceManager; import org.codehaus.plexus.resource.loader.FileResourceCreationException; import org.codehaus.plexus.resource.loader.FileResourceLoader; import org.codehaus.plexus.resource.loader.ResourceNotFoundException; import org.codehaus.plexus.util.FileUtils; import org.codehaus.plexus.util.StringUtils; import com.puppycrawl.tools.checkstyle.Checker; import com.puppycrawl.tools.checkstyle.ConfigurationLoader; import com.puppycrawl.tools.checkstyle.DefaultConfiguration; import com.puppycrawl.tools.checkstyle.PackageNamesLoader; import com.puppycrawl.tools.checkstyle.PropertiesExpander; import com.puppycrawl.tools.checkstyle.api.AuditListener; import com.puppycrawl.tools.checkstyle.api.CheckstyleException; import com.puppycrawl.tools.checkstyle.api.Configuration; import com.puppycrawl.tools.checkstyle.api.FilterSet; import com.puppycrawl.tools.checkstyle.filters.SuppressionsLoader; /** * @author Olivier Lamy * @since 2.5 * @version $Id$ */ @Component(role = CheckstyleExecutor.class, hint = "default", instantiationStrategy = "per-lookup") public class DefaultCheckstyleExecutor extends AbstractLogEnabled implements CheckstyleExecutor { @Requirement(hint = "default") private ResourceManager locator; @Requirement(hint = "license") private ResourceManager licenseLocator; public CheckstyleResults executeCheckstyle(CheckstyleExecutorRequest request) throws CheckstyleExecutorException, CheckstyleException { // Checkstyle will always use the context classloader in order // to load resources (dtds), // so we have to fix it // olamy this hack is not anymore needed in Maven 3.x ClassLoader checkstyleClassLoader = PackageNamesLoader.class.getClassLoader(); Thread.currentThread().setContextClassLoader(checkstyleClassLoader); if (getLogger().isDebugEnabled()) { getLogger().debug("executeCheckstyle start headerLocation : " + request.getHeaderLocation()); } MavenProject project = request.getProject(); configureResourceLocator(locator, request, null); configureResourceLocator(licenseLocator, request, request.getLicenseArtifacts()); // Config is less critical than License, locator can still be used. // configureResourceLocator( configurationLocator, request, request.getConfigurationArtifacts() ); List<File> files; try { files = getFilesToProcess(request); } catch (IOException e) { throw new CheckstyleExecutorException("Error getting files to process", e); } final String suppressionsFilePath = getSuppressionsFilePath(request); FilterSet filterSet = getSuppressionsFilterSet(suppressionsFilePath); Checker checker = new Checker(); // setup classloader, needed to avoid "Unable to get class information for ..." errors List<String> classPathStrings = new ArrayList<String>(); List<String> outputDirectories = new ArrayList<String>(); // stand-alone Collection<File> sourceDirectories = null; Collection<File> testSourceDirectories = request.getTestSourceDirectories(); // aggregator Map<MavenProject, Collection<File>> sourceDirectoriesByProject = new HashMap<MavenProject, Collection<File>>(); Map<MavenProject, Collection<File>> testSourceDirectoriesByProject = new HashMap<MavenProject, Collection<File>>(); if (request.isAggregate()) { for (MavenProject childProject : request.getReactorProjects()) { sourceDirectories = new ArrayList<File>(childProject.getCompileSourceRoots().size()); List<String> compileSourceRoots = childProject.getCompileSourceRoots(); for (String compileSourceRoot : compileSourceRoots) { sourceDirectories.add(new File(compileSourceRoot)); } sourceDirectoriesByProject.put(childProject, sourceDirectories); testSourceDirectories = new ArrayList<File>(childProject.getTestCompileSourceRoots().size()); List<String> testCompileSourceRoots = childProject.getTestCompileSourceRoots(); for (String testCompileSourceRoot : testCompileSourceRoots) { testSourceDirectories.add(new File(testCompileSourceRoot)); } testSourceDirectoriesByProject.put(childProject, testSourceDirectories); prepareCheckstylePaths(request, childProject, classPathStrings, outputDirectories, sourceDirectories, testSourceDirectories); } } else { sourceDirectories = request.getSourceDirectories(); prepareCheckstylePaths(request, project, classPathStrings, outputDirectories, sourceDirectories, testSourceDirectories); } final List<URL> urls = new ArrayList<URL>(classPathStrings.size()); for (String path : classPathStrings) { try { urls.add(new File(path).toURL()); } catch (MalformedURLException e) { throw new CheckstyleExecutorException(e.getMessage(), e); } } for (String outputDirectoryString : outputDirectories) { try { if (outputDirectoryString != null) { File outputDirectoryFile = new File(outputDirectoryString); if (outputDirectoryFile.exists()) { URL outputDirectoryUrl = outputDirectoryFile.toURL(); getLogger().debug("Adding the outputDirectory " + outputDirectoryUrl.toString() + " to the Checkstyle class path"); urls.add(outputDirectoryUrl); } } } catch (MalformedURLException e) { throw new CheckstyleExecutorException(e.getMessage(), e); } } URLClassLoader projectClassLoader = AccessController.doPrivileged(new PrivilegedAction<URLClassLoader>() { public URLClassLoader run() { return new URLClassLoader(urls.toArray(new URL[urls.size()]), null); } }); checker.setClassloader(projectClassLoader); checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader()); if (filterSet != null) { checker.addFilter(filterSet); } Configuration configuration = getConfiguration(request); checker.configure(configuration); AuditListener listener = request.getListener(); if (listener != null) { checker.addListener(listener); } if (request.isConsoleOutput()) { checker.addListener(request.getConsoleListener()); } CheckstyleCheckerListener checkerListener = new CheckstyleCheckerListener(configuration); if (request.isAggregate()) { for (MavenProject childProject : request.getReactorProjects()) { sourceDirectories = sourceDirectoriesByProject.get(childProject); testSourceDirectories = testSourceDirectoriesByProject.get(childProject); addSourceDirectory(checkerListener, sourceDirectories, testSourceDirectories, childProject.getResources(), request); } } else { addSourceDirectory(checkerListener, sourceDirectories, testSourceDirectories, request.getResources(), request); } checker.addListener(checkerListener); int nbErrors = checker.process(files); checker.destroy(); if (projectClassLoader instanceof Closeable) { try { ((Closeable) projectClassLoader).close(); } catch (IOException ex) { // Nothing we can do - and not detrimental to the build (save running out of file handles). getLogger().info("Failed to close custom Classloader - this indicated a bug in the code.", ex); } } if (request.getStringOutputStream() != null) { String message = request.getStringOutputStream().toString().trim(); if (message.length() > 0) { getLogger().info(message); } } if (nbErrors > 0) { String message = "There are " + nbErrors + " checkstyle errors."; if (request.isFailsOnError()) { // TODO: should be a failure, not an error. Report is not meant to // throw an exception here (so site would // work regardless of config), but should record this information throw new CheckstyleExecutorException(message); } else { getLogger().info(message); } } return checkerListener.getResults(); } protected void addSourceDirectory(CheckstyleCheckerListener sinkListener, Collection<File> sourceDirectories, Collection<File> testSourceDirectories, List<Resource> resources, CheckstyleExecutorRequest request) { if (sourceDirectories != null) { for (File sourceDirectory : sourceDirectories) { if (sourceDirectory.exists()) { sinkListener.addSourceDirectory(sourceDirectory); } } } if (request.isIncludeTestSourceDirectory() && (testSourceDirectories != null)) { for (File testSourceDirectory : testSourceDirectories) { if (testSourceDirectory.isDirectory()) { sinkListener.addSourceDirectory(testSourceDirectory); } } } if (resources != null) { for (Resource resource : resources) { if (resource.getDirectory() != null) { File resourcesDirectory = new File(resource.getDirectory()); if (resourcesDirectory.exists() && resourcesDirectory.isDirectory()) { sinkListener.addSourceDirectory(resourcesDirectory); getLogger().debug( "Added '" + resourcesDirectory.getAbsolutePath() + "' as a source directory."); } } } } } public Configuration getConfiguration(CheckstyleExecutorRequest request) throws CheckstyleExecutorException { try { // Checkstyle will always use the context classloader in order // to load resources (dtds), // so we have to fix it ClassLoader checkstyleClassLoader = PackageNamesLoader.class.getClassLoader(); Thread.currentThread().setContextClassLoader(checkstyleClassLoader); String configFile = getConfigFile(request); Properties overridingProperties = getOverridingProperties(request); Configuration config = ConfigurationLoader.loadConfiguration(configFile, new PropertiesExpander(overridingProperties)); String effectiveEncoding = StringUtils.isNotEmpty(request.getEncoding()) ? request.getEncoding() : System.getProperty("file.encoding", "UTF-8"); if (StringUtils.isEmpty(request.getEncoding())) { getLogger().warn("File encoding has not been set, using platform encoding " + effectiveEncoding + ", i.e. build is platform dependent!"); } if ("Checker".equals(config.getName()) || "com.puppycrawl.tools.checkstyle.Checker".equals(config.getName())) { if (config instanceof DefaultConfiguration) { // MCHECKSTYLE-173 Only add the "charset" attribute if it has not been set try { if (config.getAttribute("charset") == null) { ((DefaultConfiguration) config).addAttribute("charset", effectiveEncoding); } } catch (CheckstyleException ex) { // Checkstyle 5.4+ throws an exception when trying to access an attribute that doesn't exist ((DefaultConfiguration) config).addAttribute("charset", effectiveEncoding); } } else { getLogger().warn("Failed to configure file encoding on module " + config); } } Configuration[] modules = config.getChildren(); for (Configuration module : modules) { if ("TreeWalker".equals(module.getName()) || "com.puppycrawl.tools.checkstyle.TreeWalker".equals(module.getName())) { if (module instanceof DefaultConfiguration) { // MCHECKSTYLE-132 DefaultConfiguration addAttribute has changed in checkstyle 5.3 try { if (module.getAttribute("cacheFile") == null) { ((DefaultConfiguration) module).addAttribute("cacheFile", request.getCacheFile()); } } catch (CheckstyleException ex) { // MCHECKSTYLE-159 - checkstyle 5.4 throws an exception instead of return null if // "cacheFile" // doesn't exist ((DefaultConfiguration) module).addAttribute("cacheFile", request.getCacheFile()); } } else { getLogger().warn("Failed to configure cache file on module " + module); } } } return config; } catch (CheckstyleException e) { throw new CheckstyleExecutorException("Failed during checkstyle configuration", e); } } private void prepareCheckstylePaths(CheckstyleExecutorRequest request, MavenProject project, List<String> classPathStrings, List<String> outputDirectories, Collection<File> sourceDirectories, Collection<File> testSourceDirectories) throws CheckstyleExecutorException { try { outputDirectories.add(project.getBuild().getOutputDirectory()); if (request.isIncludeTestSourceDirectory() && (testSourceDirectories != null) && anyDirectoryExists(testSourceDirectories)) { classPathStrings.addAll(project.getTestClasspathElements()); outputDirectories.add(project.getBuild().getTestOutputDirectory()); } else { classPathStrings.addAll(project.getCompileClasspathElements()); } } catch (DependencyResolutionRequiredException e) { throw new CheckstyleExecutorException(e.getMessage(), e); } } private boolean anyDirectoryExists(Collection<File> files) { for (File file : files) { if (file.isDirectory()) { return true; } } return false; } private Properties getOverridingProperties(CheckstyleExecutorRequest request) throws CheckstyleExecutorException { Properties p = new Properties(); try { if (request.getPropertiesLocation() != null) { if (getLogger().isDebugEnabled()) { getLogger().debug("request.getPropertiesLocation() " + request.getPropertiesLocation()); } File propertiesFile = locator.getResourceAsFile(request.getPropertiesLocation(), "checkstyle-checker.properties"); FileInputStream properties = new FileInputStream(propertiesFile); try { if (propertiesFile != null) { p.load(properties); } } finally { IOUtils.closeQuietly(properties); } } if (StringUtils.isNotEmpty(request.getPropertyExpansion())) { String propertyExpansion = request.getPropertyExpansion(); // Convert \ to \\, so that p.load will convert it back properly propertyExpansion = StringUtils.replace(propertyExpansion, "\\", "\\\\"); p.load(new ByteArrayInputStream(propertyExpansion.getBytes())); } // Workaround for MCHECKSTYLE-48 // Make sure that "config/maven-header.txt" is the default value // for headerLocation, if configLocation="config/maven_checks.xml" String headerLocation = request.getHeaderLocation(); if ("config/maven_checks.xml".equals(request.getConfigLocation())) { if ("LICENSE.txt".equals(request.getHeaderLocation())) { headerLocation = "config/maven-header.txt"; } } if (getLogger().isDebugEnabled()) { getLogger().debug("headerLocation " + headerLocation); } if (StringUtils.isNotEmpty(headerLocation)) { try { File headerFile = licenseLocator.getResourceAsFile(headerLocation, "checkstyle-header.txt"); if (headerFile != null) { p.setProperty("checkstyle.header.file", headerFile.getAbsolutePath()); } } catch (FileResourceCreationException e) { getLogger().debug("Unable to process header location: " + headerLocation); getLogger().debug("Checkstyle will throw exception if ${checkstyle.header.file} is used"); } catch (ResourceNotFoundException e) { getLogger().debug("Unable to process header location: " + headerLocation); getLogger().debug("Checkstyle will throw exception if ${checkstyle.header.file} is used"); } } if (request.getCacheFile() != null) { p.setProperty("checkstyle.cache.file", request.getCacheFile()); } } catch (IOException e) { throw new CheckstyleExecutorException("Failed to get overriding properties", e); } catch (FileResourceCreationException e) { throw new CheckstyleExecutorException("Failed to get overriding properties", e); } catch (ResourceNotFoundException e) { throw new CheckstyleExecutorException("Failed to get overriding properties", e); } if (request.getSuppressionsFileExpression() != null) { String suppressionsFilePath = getSuppressionsFilePath(request); if (suppressionsFilePath != null) { p.setProperty(request.getSuppressionsFileExpression(), suppressionsFilePath); } } return p; } private List<File> getFilesToProcess(CheckstyleExecutorRequest request) throws IOException { StringBuilder excludesStr = new StringBuilder(); if (StringUtils.isNotEmpty(request.getExcludes())) { excludesStr.append(request.getExcludes()); } String[] defaultExcludes = FileUtils.getDefaultExcludes(); for (String defaultExclude : defaultExcludes) { if (excludesStr.length() > 0) { excludesStr.append(","); } excludesStr.append(defaultExclude); } Set<File> files = new LinkedHashSet<File>(); if (request.isAggregate()) { for (MavenProject project : request.getReactorProjects()) { Set<File> sourceDirectories = new LinkedHashSet<File>(); // CompileSourceRoots are absolute paths List<String> compileSourceRoots = project.getCompileSourceRoots(); for (String compileSourceRoot : compileSourceRoots) { sourceDirectories.add(new File(compileSourceRoot)); } Set<File> testSourceDirectories = new LinkedHashSet<File>(); // CompileSourceRoots are absolute paths List<String> testCompileSourceRoots = project.getTestCompileSourceRoots(); for (String testCompileSourceRoot : testCompileSourceRoots) { testSourceDirectories.add(new File(testCompileSourceRoot)); } addFilesToProcess(request, sourceDirectories, project.getResources(), project.getTestResources(), files, testSourceDirectories); } } else { Collection<File> sourceDirectories = request.getSourceDirectories(); addFilesToProcess(request, sourceDirectories, request.getResources(), request.getTestResources(), files, request.getTestSourceDirectories()); } getLogger().debug("Added " + files.size() + " files to process."); return new ArrayList<File>(files); } private void addFilesToProcess(CheckstyleExecutorRequest request, Collection<File> sourceDirectories, List<Resource> resources, List<Resource> testResources, Collection<File> files, Collection<File> testSourceDirectories) throws IOException { if (sourceDirectories != null) { for (File sourceDirectory : sourceDirectories) { if (sourceDirectory.isDirectory()) { final List<File> sourceFiles = FileUtils.getFiles(sourceDirectory, request.getIncludes(), request.getExcludes()); files.addAll(sourceFiles); getLogger().debug("Added " + sourceFiles.size() + " source files found in '" + sourceDirectory.getAbsolutePath() + "'."); } } } if (request.isIncludeTestSourceDirectory() && testSourceDirectories != null) { for (File testSourceDirectory : testSourceDirectories) { if (testSourceDirectory.isDirectory()) { final List<File> testSourceFiles = FileUtils.getFiles(testSourceDirectory, request.getIncludes(), request.getExcludes()); files.addAll(testSourceFiles); getLogger().debug("Added " + testSourceFiles.size() + " test source files found in '" + testSourceDirectory.getAbsolutePath() + "'."); } } } if (resources != null && request.isIncludeResources()) { addResourceFilesToProcess(request, resources, files); } else { getLogger().debug("No resources found in this project."); } if (testResources != null && request.isIncludeTestResources()) { addResourceFilesToProcess(request, testResources, files); } else { getLogger().debug("No test resources found in this project."); } } private void addResourceFilesToProcess(CheckstyleExecutorRequest request, List<Resource> resources, Collection<File> files) throws IOException { for (Resource resource : resources) { if (resource.getDirectory() != null) { File resourcesDirectory = new File(resource.getDirectory()); if (resourcesDirectory.isDirectory()) { String includes = request.getResourceIncludes(); String excludes = request.getResourceExcludes(); // MCHECKSTYLE-214: Only with project-root respect in/excludes, otherwise you'll get every file if (resourcesDirectory.equals(request.getProject().getBasedir())) { String resourceIncludes = StringUtils.join(resource.getIncludes().iterator(), ","); if (StringUtils.isEmpty(includes)) { includes = resourceIncludes; } else { includes += "," + resourceIncludes; } String resourceExcludes = StringUtils.join(resource.getExcludes().iterator(), ","); if (StringUtils.isEmpty(excludes)) { excludes = resourceExcludes; } else { excludes += "," + resourceExcludes; } } List<File> resourceFiles = FileUtils.getFiles(resourcesDirectory, includes, excludes); files.addAll(resourceFiles); getLogger().debug("Added " + resourceFiles.size() + " resource files found in '" + resourcesDirectory.getAbsolutePath() + "'."); } else { getLogger().debug("The resources directory '" + resourcesDirectory.getAbsolutePath() + "' does not exist or is not a directory."); } } } } private FilterSet getSuppressionsFilterSet(final String suppressionsFilePath) throws CheckstyleExecutorException { if (suppressionsFilePath == null) { return null; } try { return SuppressionsLoader.loadSuppressions(suppressionsFilePath); } catch (CheckstyleException ce) { throw new CheckstyleExecutorException("Failed to load suppressions file from: " + suppressionsFilePath, ce); } } private String getSuppressionsFilePath(final CheckstyleExecutorRequest request) throws CheckstyleExecutorException { final String suppressionsLocation = request.getSuppressionsLocation(); if (StringUtils.isEmpty(suppressionsLocation)) { return null; } try { File suppressionsFile = locator.getResourceAsFile(suppressionsLocation, "checkstyle-suppressions.xml"); return suppressionsFile == null ? null : suppressionsFile.getAbsolutePath(); } catch (ResourceNotFoundException e) { throw new CheckstyleExecutorException( "Unable to find suppressions file at location: " + suppressionsLocation, e); } catch (FileResourceCreationException e) { throw new CheckstyleExecutorException( "Unable to process suppressions file location: " + suppressionsLocation, e); } } private String getConfigFile(CheckstyleExecutorRequest request) throws CheckstyleExecutorException { try { if (getLogger().isDebugEnabled()) { getLogger().debug("request.getConfigLocation() " + request.getConfigLocation()); } File configFile = locator.getResourceAsFile(request.getConfigLocation(), "checkstyle-checker.xml"); if (configFile == null) { throw new CheckstyleExecutorException( "Unable to process config location: " + request.getConfigLocation()); } return configFile.getAbsolutePath(); } catch (ResourceNotFoundException e) { throw new CheckstyleExecutorException( "Unable to find configuration file at location: " + request.getConfigLocation(), e); } catch (FileResourceCreationException e) { throw new CheckstyleExecutorException( "Unable to process configuration file at location: " + request.getConfigLocation(), e); } } /** * Configures search paths in the resource locator. * This method should only be called once per execution. * * @param request executor request data. */ private void configureResourceLocator(final ResourceManager resourceManager, final CheckstyleExecutorRequest request, final List<Artifact> additionalArtifacts) { final MavenProject project = request.getProject(); resourceManager.setOutputDirectory(new File(project.getBuild().getDirectory())); // Recurse up the parent hierarchy and add project directories to the search roots MavenProject parent = project; while (parent != null && parent.getFile() != null) { // MCHECKSTYLE-131 ( olamy ) I don't like this hack. // (dkulp) Me either. It really pollutes the location stuff // by allowing searches of stuff outside the current module. File dir = parent.getFile().getParentFile(); resourceManager.addSearchPath(FileResourceLoader.ID, dir.getAbsolutePath()); parent = parent.getParent(); } resourceManager.addSearchPath("url", ""); // MCHECKSTYLE-225: load licenses from additional artifacts, not from classpath if (additionalArtifacts != null) { for (Artifact licenseArtifact : additionalArtifacts) { try { resourceManager.addSearchPath("jar", "jar:" + licenseArtifact.getFile().toURI().toURL()); } catch (MalformedURLException e) { // noop } } } } }