org.codehaus.mojo.aspectj.AbstractAjcCompiler.java Source code

Java tutorial

Introduction

Here is the source code for org.codehaus.mojo.aspectj.AbstractAjcCompiler.java

Source

package org.codehaus.mojo.aspectj;

/**
 * The MIT License
 *
 * Copyright 2005-2006 The Codehaus.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is furnished to do
 * so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.artifact.handler.ArtifactHandler;
import org.apache.maven.plugin.MojoExecutionException;
import org.aspectj.bridge.IMessage;
import org.aspectj.tools.ajc.Main;
import org.codehaus.plexus.util.FileUtils;

/**
 * Base class for the two aspectJ compiletime weaving mojos.
 * 
 * @author <a href="mailto:kaare.nilsen@gmail.com">Kaare Nilsen</a>
 */
public abstract class AbstractAjcCompiler extends AbstractAjcMojo {

    /**
     * The source directory for the aspects
     * @parameter default-value="src/main/aspect"
     */
    protected String aspectDirectory = "src/main/aspect";

    /**
     * The source directory for the test aspects
     * @parameter default-value="src/test/aspect"
     */
    protected String testAspectDirectory = "src/test/aspect";

    /**
     * List of ant-style patterns used to specify the aspects that should be included when 
     * compiling. When none specified all .java and .aj files in the project source directories, or
     * directories specified by the ajdtDefFile property are included.
     * 
     * * @parameter
     */
    protected String[] includes;

    /**
     * List of ant-style patterns used to specify the aspects that should be excluded when 
     * compiling. When none specified all .java and .aj files in the project source directories, or
     * directories spesified by the ajdtDefFile property are included.
     * 
     * * @parameter
     */
    protected String[] excludes;

    /**
     * Where to find the ajdt build definition file.
     * <i>If set this will override the use of project sourcedirs</i>.
     * 
     * @parameter
     */
    protected String ajdtBuildDefFile;

    /**
     * Generate aop.xml file for load-time weaving with default name.(/META-INF/aop.xml)
     * 
     * @parameter
     */
    protected boolean outxml;

    /**
     * Generate aop.xml file for load-time weaving with custom name.
     * 
     * @parameter
     */
    protected String outxmlfile;

    /**
     * Generate .ajesym symbol files for emacs support
     * 
     *  @parameter
     */
    protected boolean emacssym;

    /**
     * Set default level for messages about potential 
     * programming mistakes in crosscutting code. 
     * {level} may be ignore, warning, or error. 
     * This overrides entries in org/aspectj/weaver/XlintDefault.properties 
     * from aspectjtools.jar.
     * 
     *  @parameter
     */
    protected String Xlint;

    /**
     * Specify classfile target setting (1.1 to 1.6) default is 1.2
     * 
     *  @parameter
     */
    protected String target;

    /**
     * Toggle assertions (1.3, 1.4, or 1.6 - default is 1.4). 
     * When using -source 1.3, an assert() statement valid under Java 1.4 
     * will result in a compiler error. When using -source 1.4, 
     * treat assert as a keyword and implement assertions 
     * according to the 1.4 language spec. 
     * When using -source 1.5 or higher, Java 5 language features are permitted.
     * 
     *  @parameter
     */
    protected String source;

    /**
     * Specify compiler compliance setting (1.3 to 1.6)
     * default is 1.4
     * 
     *  @parameter
     */
    protected String complianceLevel;

    /**
     * Toggle warningmessages on deprecations
     * 
     * @parameter
     */
    protected boolean deprecation;

    /**
     * Emit no errors for unresolved imports;
     * 
     * @parameter
     */
    protected boolean noImportError;

    /**
     * Keep compiling after error, dumping class files with problem methods
     * 
     * @parameter
     */
    protected boolean proceedOnError;

    /**
     * Preserve all local variables during code generation (to facilitate debugging).
     * 
     * @parameter
     */
    protected boolean preserveAllLocals;

    /**
     * Compute reference information.
     * 
     * @parameter
     */
    protected boolean referenceInfo;

    /**
     * Specify default source encoding format. 
     *      
     * @parameter expression="${project.build.sourceEncoding}"
     */
    protected String encoding;

    /**
     * Emit messages about accessed/processed compilation units
     * 
     * @parameter
     */
    protected boolean verbose;

    /**
     * Emit messages about weaving
     * 
     * @parameter
     */
    protected boolean showWeaveInfo;

    /**
     * Repeat compilation process N times (typically to do performance analysis).
     * 
     * @parameter
     */
    protected int repeat;

    /**
     * (Experimental) runs weaver in reweavable mode which causes it to create 
     * woven classes that can be rewoven, subject to the restriction 
     * that on attempting a reweave all the types that advised the woven 
     * type must be accessible.
     * 
     * @parameter
     */
    protected boolean Xreweavable;

    /**
     * (Experimental) do not inline around advice
     * 
     * @parameter
     */
    protected boolean XnoInline;

    /**
     * (Experimental) Normally it is an error to declare aspects Serializable. This option removes that restriction.
     * 
     * @parameter
     */
    protected boolean XserializableAspects;

    /**
     * Causes the compiler to calculate and add the SerialVersionUID field to any type implementing Serializable that is affected by an aspect. 
     * The field is calculated based on the class before weaving has taken place.
     * 
     * @parameter
     */
    protected boolean XaddSerialVersionUID;

    /**
     * Override location of VM's bootclasspath for purposes of evaluating types when compiling. 
     * Path is a single argument containing a list of paths to zip files or directories, delimited by the platform-specific path delimiter.
     *
     * @parameter
     */
    protected String bootclasspath;

    /**
     * Emit warnings for any instances of the comma-delimited list of questionable code (eg 'unusedLocals,deprecation'):
     * see http://www.eclipse.org/aspectj/doc/released/devguide/ajc-ref.html#ajc for available settings
     * @parameter
     */
    protected String warn;

    /**
     * The filename to store build configuration in.
     * This file will be placed in the project build output
     * directory, and will contain all the arguments
     * passed to the compiler in the last run, and also
     * all the filenames included in the build. Aspects as
     * well as java files.
     * 
     * @parameter default-value="builddef.lst"
     */
    protected String argumentFileName = "builddef.lst";

    /**
     * Holder for ajc compiler options
     */
    protected List ajcOptions = new ArrayList();

    /**
     * Holds all files found using the includes, excludes parameters.
     */
    protected Set resolvedIncludes;

    /**
     * Abstract method used by child classes to spesify the correct output
     * directory for compiled classes.
     * 
     * @return where compiled classes should be put.
     */
    protected abstract List getOutputDirectories();

    /**
     * Abstract method used by child classes to spesify the correct source directory for classes.
     * 
     * @return where sources may be found.
     */
    protected abstract List getSourceDirectories();

    /**
     * Abstract method used by cild classes to specify aditional aspect paths.
     * @return
     */
    protected abstract String getAdditionalAspectPaths();

    /**
     * Do the AspectJ compiling.
     * 
     * @throws MojoExecutionException
     */
    public void execute() throws MojoExecutionException {
        ArtifactHandler artifactHandler = project.getArtifact().getArtifactHandler();
        if (!"java".equals(artifactHandler.getLanguage())) {
            getLog().debug("Not executing aspectJ compiler as the project is not a Java classpath-capable package");
            return;
        }

        Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
        project.getCompileSourceRoots().add(basedir.getAbsolutePath() + "/" + aspectDirectory);
        project.getTestCompileSourceRoots().add(basedir.getAbsolutePath() + "/" + testAspectDirectory);
        assembleArguments();

        if ((weaveDirectories == null) && !hasSourcesToCompile()) {
            getLog().debug("No sources found skipping aspectJ compile");
            return;
        }

        /* TODO actually check weaveDirectories to see if something has changed */
        if ((weaveDirectories == null) && !isBuildNeeded()) {
            getLog().debug("No modifications found skipping aspectJ compile");
            return;
        }

        getLog().debug("Starting compiling aspects");
        if (getLog().isDebugEnabled()) {
            String command = "Running : ajc ";
            Iterator iter = ajcOptions.iterator();
            while (iter.hasNext()) {
                command += (iter.next() + " ");
            }
            getLog().debug(command);
        }
        try {
            File outDir = new File((String) getOutputDirectories().get(0));
            AjcHelper.writeBuildConfigToFile(ajcOptions, argumentFileName, outDir);
            getLog().debug("Argumentsfile written : "
                    + new File(outDir.getAbsolutePath() + argumentFileName).getAbsolutePath());
        } catch (IOException e) {
            throw new MojoExecutionException("Could not write arguments file to the target area");
        }
        Main main = new Main();
        MavenMessageHandler mavenMessageHandler = new MavenMessageHandler(getLog());
        main.setHolder(mavenMessageHandler);

        main.runMain((String[]) ajcOptions.toArray(new String[0]), false);
        IMessage[] errors = mavenMessageHandler.getErrors();
        if (errors.length > 0) {
            throw new CompilationFailedException(errors);
        }
    }

    /**
     * Assembles a complete ajc compiler arguments list.
     *
     * @throws MojoExecutionException error in configuration
     */
    protected void assembleArguments() throws MojoExecutionException {
        // Add classpath
        ajcOptions.add("-classpath");
        ajcOptions.add(AjcHelper.createClassPath(project, null, getOutputDirectories()));

        // Add boot classpath
        if (null != bootclasspath) {
            ajcOptions.add("-bootclasspath");
            ajcOptions.add(bootclasspath);
        }

        // Add warn option
        if (null != warn) {
            ajcOptions.add("-warn:" + warn);
        }

        // Add artifacts or directories to weave
        String joinedWeaveDirectories = null;
        if (weaveDirectories != null) {
            joinedWeaveDirectories = StringUtils.join(weaveDirectories, File.pathSeparator);
        }
        addModulesArgument("-inpath", ajcOptions, weaveDependencies, joinedWeaveDirectories,
                "dependencies and/or directories to weave");

        // Add library artifacts 
        addModulesArgument("-aspectpath", ajcOptions, aspectLibraries, getAdditionalAspectPaths(),
                "an aspect library");

        //add target dir argument
        ajcOptions.add("-d");
        ajcOptions.add(getOutputDirectories().get(0));

        // Add all the files to be included in the build,
        if (null != ajdtBuildDefFile) {
            resolvedIncludes = AjcHelper.getBuildFilesForAjdtFile(ajdtBuildDefFile, basedir);
        } else {
            resolvedIncludes = AjcHelper.getBuildFilesForSourceDirs(getSourceDirectories(), this.includes,
                    this.excludes);
        }
        ajcOptions.addAll(resolvedIncludes);
    }

    /**
     * Finds all artifacts in the weavemodule property,
     * and adds them to the ajc options.
     *  
     * @param arguments
     * @throws MojoExecutionException
     */
    private void addModulesArgument(String argument, List arguments, Module[] modules, String aditionalpath,
            String role) throws MojoExecutionException {
        StringBuffer buf = new StringBuffer();

        if (null != aditionalpath) {
            arguments.add(argument);
            buf.append(aditionalpath);
        }
        if (modules != null && modules.length > 0) {
            if (!arguments.contains(argument)) {
                arguments.add(argument);
            }

            for (int i = 0; i < modules.length; ++i) {
                Module module = modules[i];
                String key = ArtifactUtils.versionlessKey(module.getGroupId(), module.getArtifactId());
                Artifact artifact = (Artifact) project.getArtifactMap().get(key);
                if (artifact == null) {
                    throw new MojoExecutionException("The artifact " + key + " referenced in aspectj plugin as "
                            + role + ", is not found the project dependencies");
                }
                if (buf.length() != 0) {
                    buf.append(File.pathSeparatorChar);
                }
                buf.append(artifact.getFile().getPath());
            }
        }
        if (buf.length() > 0) {
            String pathString = buf.toString();
            arguments.add(pathString);
            getLog().debug("Adding " + argument + ": " + pathString);
        }
    }

    /**
     * Checks modifications that would make us need a build
     * 
     * @throws MojoExecutionException 
     *
     */
    protected boolean isBuildNeeded() throws MojoExecutionException {
        File outDir = new File(getOutputDirectories().get(0).toString());
        return hasNoPreviousBuild(outDir) || hasArgumentsChanged(outDir) || hasSourcesChanged(outDir);

    }

    private boolean hasNoPreviousBuild(File outDir) {
        return (!FileUtils.fileExists(new File(outDir.getAbsolutePath(), argumentFileName).getAbsolutePath()));
    }

    private boolean hasArgumentsChanged(File outDir) throws MojoExecutionException {
        try {
            return (!ajcOptions.equals(AjcHelper.readBuildConfigFile(argumentFileName, outDir)));
        } catch (IOException e) {
            throw new MojoExecutionException("Error during reading of previous argumentsfile ");
        }
    }

    /**
     * Not entirely safe, assembleArguments() must be run 
     */
    private boolean hasSourcesToCompile() {
        return resolvedIncludes.size() > 0;
    }

    private boolean hasSourcesChanged(File outDir) {
        Iterator sourceIter = resolvedIncludes.iterator();
        long lastBuild = new File(outDir.getAbsolutePath(), argumentFileName).lastModified();
        while (sourceIter.hasNext()) {
            File sourceFile = new File((String) sourceIter.next());
            long sourceModified = sourceFile.lastModified();
            if (sourceModified >= lastBuild) {
                return true;
            }

        }
        return false;
    }

    /** 
     * Setters which when called sets compiler arguments
     */
    public void setComplianceLevel(String complianceLevel) {
        if (complianceLevel.equals("1.3") || complianceLevel.equals("1.4") || complianceLevel.equals("1.5")
                || complianceLevel.equals("1.6")) {
            ajcOptions.add("-" + complianceLevel);
        }

    }

    public void setDeprecation(boolean deprecation) {
        if (deprecation) {
            ajcOptions.add("-deprecation");
        }
    }

    public void setEmacssym(boolean emacssym) {
        if (emacssym) {
            ajcOptions.add("-emacssym");
        }

    }

    public void setEncoding(String encoding) {
        ajcOptions.add("-encoding");
        ajcOptions.add(encoding);
    }

    public void setNoImportError(boolean noImportError) {
        if (noImportError) {
            ajcOptions.add("-noImportError");
        }

    }

    public void setOutxml(boolean outxml) {
        if (outxml) {
            ajcOptions.add("-outxml");
        }

    }

    public void setOutxmlfile(String outxmlfile) {
        ajcOptions.add("-outxmlfile");
        ajcOptions.add(outxmlfile);
    }

    public void setPreserveAllLocals(boolean preserveAllLocals) {
        if (preserveAllLocals) {
            ajcOptions.add("-preserveAllLocals");
        }

    }

    public void setProceedOnError(boolean proceedOnError) {
        if (proceedOnError) {
            ajcOptions.add("-proceedOnError");
        }

    }

    public void setReferenceInfo(boolean referenceInfo) {
        if (referenceInfo) {
            ajcOptions.add("-referenceInfo");
        }

    }

    public void setRepeat(int repeat) {
        ajcOptions.add("-repeat");
        ajcOptions.add("" + repeat);
    }

    public void setShowWeaveInfo(boolean showWeaveInfo) {
        if (showWeaveInfo) {
            ajcOptions.add("-showWeaveInfo");
        }

    }

    public void setTarget(String target) {
        ajcOptions.add("-target");
        ajcOptions.add(target);
    }

    public void setSource(String source) {
        ajcOptions.add("-source");
        ajcOptions.add(source);
    }

    public void setVerbose(boolean verbose) {
        if (verbose) {
            ajcOptions.add("-verbose");
        }

    }

    public void setXlint(String xlint) {
        ajcOptions.add("-Xlint:" + xlint);
    }

    public void setXnoInline(boolean xnoInline) {
        if (xnoInline) {
            ajcOptions.add("-XnoInline");
        }

    }

    public void setXreweavable(boolean xreweavable) {
        if (xreweavable) {
            ajcOptions.add("-Xreweavable");
        }

    }

    public void setXserializableAspects(boolean xserializableAspects) {
        if (xserializableAspects) {
            ajcOptions.add("-XserializableAspects");
        }
    }

    public void setXaddSerialVersionUID(boolean xaddSerialVersionUID) {
        if (xaddSerialVersionUID) {
            ajcOptions.add("-XaddSerialVersionUID");
        }
    }

    public void setBootClassPath(String bootclasspath) {
        this.bootclasspath = bootclasspath;
    }

    public void setWarn(String warn) {
        this.warn = warn;
    }

    public void setArgumentFileName(String argumentFileName) {
        this.argumentFileName = argumentFileName;

    }

}