org.codehaus.mojo.sysdeo.ide.AbstractIdeSupportMojo.java Source code

Java tutorial

Introduction

Here is the source code for org.codehaus.mojo.sysdeo.ide.AbstractIdeSupportMojo.java

Source

package org.codehaus.mojo.sysdeo.ide;

/*
 * 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.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactCollector;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
import org.apache.maven.artifact.resolver.ArtifactResolver;
import org.apache.maven.artifact.resolver.DebugResolutionListener;
import org.apache.maven.artifact.resolver.ResolutionNode;
import org.apache.maven.artifact.resolver.WarningResolutionListener;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.artifact.resolver.filter.ExcludesArtifactFilter;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.DependencyManagement;
import org.apache.maven.model.Exclusion;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.logging.LogEnabled;
import org.codehaus.plexus.logging.Logger;

/**
 * Abstract base plugin which takes care of the common stuff usually needed by maven IDE plugins. A plugin extending
 * AbstractIdeSupportMojo should implement the <code>setup()</code> and <code>writeConfiguration()</code> methods, plus
 * the getters needed to get the various configuration flags and required components. The lifecycle:
 *
 * <pre>
 *       *** calls setup() where you can configure your specific stuff and stop the mojo from execute if appropriate ***
 *       - manually resolve project dependencies, NOT failing if a dependency is missing
 *       - compute project references (reactor projects) if the getUseProjectReferences() flag is set
 *       *** calls writeConfiguration(), passing the list of resolved referenced dependencies ***
 *       - report the list of missing sources or just tell how to turn this feature on if the flag was disabled
 * </pre>
 *
 * @author Fabrizio Giustina
 * @version $Id$
 */
public abstract class AbstractIdeSupportMojo extends AbstractMojo implements LogEnabled {

    /**
     * The project whose project files to create.
     *
     * @parameter expression="${project}"
     * @required
     * @readonly
     */
    private MavenProject project;

    /**
     * Artifact factory, needed to download source jars for inclusion in classpath.
     *
     * @component role="org.apache.maven.artifact.factory.ArtifactFactory"
     * @required
     * @readonly
     */
    private ArtifactFactory artifactFactory;

    /**
     * Artifact resolver, needed to download source jars for inclusion in classpath.
     *
     * @component role="org.apache.maven.artifact.resolver.ArtifactResolver"
     * @required
     * @readonly
     */
    private ArtifactResolver artifactResolver;

    /**
     * Artifact collector, needed to resolve dependencies.
     *
     * @component role="org.apache.maven.artifact.resolver.ArtifactCollector"
     * @required
     * @readonly
     */
    private ArtifactCollector artifactCollector;

    /**
     * @component role="org.apache.maven.artifact.metadata.ArtifactMetadataSource" hint="maven"
     */
    private ArtifactMetadataSource artifactMetadataSource;

    /**
     * Remote repositories which will be searched for source attachments.
     *
     * @parameter expression="${project.remoteArtifactRepositories}"
     * @required
     * @readonly
     */
    private List remoteArtifactRepositories;

    /**
     * Local maven repository.
     *
     * @parameter expression="${localRepository}"
     * @required
     * @readonly
     */
    private ArtifactRepository localRepository;

    /**
     * If the executed project is a reactor project, this will contains the full list of projects in the reactor.
     *
     * @parameter expression="${reactorProjects}"
     * @required
     * @readonly
     */
    private List reactorProjects;

    private List workspaceProjects = Collections.EMPTY_LIST;

    /**
     * Directory location of the <code>Eclipse</code> workspace.
     *
     * @parameter expression="${eclipse.workspace}"
     */
    private File workspace;

    /**
     * Plexus logger needed for debugging manual artifact resolution.
     */
    private Logger logger;

    /**
     * Getter for <code>artifactMetadataSource</code>.
     *
     * @return Returns the artifactMetadataSource.
     */
    public ArtifactMetadataSource getArtifactMetadataSource() {
        return this.artifactMetadataSource;
    }

    /**
     * Setter for <code>artifactMetadataSource</code>.
     *
     * @param artifactMetadataSource The artifactMetadataSource to set.
     */
    public void setArtifactMetadataSource(ArtifactMetadataSource artifactMetadataSource) {
        this.artifactMetadataSource = artifactMetadataSource;
    }

    /**
     * Getter for <code>project</code>.
     *
     * @return Returns the project.
     */
    public MavenProject getProject() {
        return this.project;
    }

    /**
     * Setter for <code>project</code>.
     *
     * @param project The project to set.
     */
    public void setProject(MavenProject project) {
        this.project = project;
    }

    /**
     * Getter for <code>reactorProjects</code>.
     *
     * @return Returns the reactorProjects.
     */
    public List getReactorProjects() {
        return this.reactorProjects;
    }

    /**
     * Setter for <code>reactorProjects</code>.
     *
     * @param reactorProjects The reactorProjects to set.
     */
    public void setReactorProjects(List reactorProjects) {
        this.reactorProjects = reactorProjects;
    }

    /**
     * Getter for <code>remoteArtifactRepositories</code>.
     *
     * @return Returns the remoteArtifactRepositories.
     */
    public List getRemoteArtifactRepositories() {
        return this.remoteArtifactRepositories;
    }

    /**
     * Setter for <code>remoteArtifactRepositories</code>.
     *
     * @param remoteArtifactRepositories The remoteArtifactRepositories to set.
     */
    public void setRemoteArtifactRepositories(List remoteArtifactRepositories) {
        this.remoteArtifactRepositories = remoteArtifactRepositories;
    }

    /**
     * Getter for <code>artifactFactory</code>.
     *
     * @return Returns the artifactFactory.
     */
    public ArtifactFactory getArtifactFactory() {
        return this.artifactFactory;
    }

    /**
     * Setter for <code>artifactFactory</code>.
     *
     * @param artifactFactory The artifactFactory to set.
     */
    public void setArtifactFactory(ArtifactFactory artifactFactory) {
        this.artifactFactory = artifactFactory;
    }

    /**
     * Getter for <code>artifactResolver</code>.
     *
     * @return Returns the artifactResolver.
     */
    public ArtifactResolver getArtifactResolver() {
        return this.artifactResolver;
    }

    /**
     * Setter for <code>artifactResolver</code>.
     *
     * @param artifactResolver The artifactResolver to set.
     */
    public void setArtifactResolver(ArtifactResolver artifactResolver) {
        this.artifactResolver = artifactResolver;
    }

    /**
     * Getter for <code>localRepository</code>.
     *
     * @return Returns the localRepository.
     */
    public ArtifactRepository getLocalRepository() {
        return this.localRepository;
    }

    /**
     * Setter for <code>localRepository</code>.
     *
     * @param localRepository The localRepository to set.
     */
    public void setLocalRepository(ArtifactRepository localRepository) {
        this.localRepository = localRepository;
    }

    /**
     * return <code>false</code> if projects available in a reactor build should be considered normal dependencies,
     * <code>true</code> if referenced project will be linked and not need artifact resolution.
     *
     * @return <code>true</code> if referenced project will be linked and not need artifact resolution
     */
    protected abstract boolean getUseProjectReferences();

    /**
     * Hook for preparation steps before the actual plugin execution.
     *
     * @return <code>true</code> if execution should continue or <code>false</code> if not.
     * @throws MojoExecutionException generic mojo exception
     */
    protected abstract boolean setup() throws MojoExecutionException;

    /**
     * Main plugin method where dependencies should be processed in order to generate IDE configuration files.
     *
     * @param deps list of <code>IdeDependency</code> objects, with artifacts, sources and javadocs already resolved
     * @throws MojoExecutionException generic mojo exception
     */
    protected abstract void writeConfiguration(IdeDependency[] deps) throws MojoExecutionException;

    /**
     * @see org.apache.maven.plugin.Mojo#execute()
     */
    public final void execute() throws MojoExecutionException, MojoFailureException {
        boolean processProject = setup();
        if (!processProject) {
            return;
        }

        // resolve artifacts
        IdeDependency[] deps = doDependencyResolution();

        writeConfiguration(deps);

    }

    /**
     * {@inheritDoc}
     *
     * @see org.codehaus.plexus.logging.LogEnabled#enableLogging(org.codehaus.plexus.logging.Logger)
     */
    public void enableLogging(Logger logger) {
        this.logger = logger;
    }

    /**
     * Resolve project dependencies. Manual resolution is needed in order to avoid resoltion of multiproject artifacts
     * (if projects will be linked each other an installed jar is not needed) and to avoid a failure when a jar is
     * missing.
     *
     * @throws MojoExecutionException if dependencies can't be resolved
     * @return resoved IDE dependencies, with attached jars for non-reactor dependencies
     */
    protected IdeDependency[] doDependencyResolution() throws MojoExecutionException {

        if (workspace != null) {
            getLog().info("read available projects in eclipse workspace");
            workspaceProjects = new ReadWorkspaceLocations().readWorkspace(workspace, getLog());
        }

        ArtifactRepository localRepo = getLocalRepository();

        List dependencies = getProject().getDependencies();

        // Collect the list of resolved IdeDependencies.
        List dependencyList = new ArrayList();

        if (dependencies != null) {
            Map managedVersions = createManagedVersionMap(project.getId(), project.getDependencyManagement());

            ArtifactResolutionResult artifactResolutionResult = null;

            try {

                List listeners = new ArrayList();

                if (logger.isDebugEnabled()) {
                    listeners.add(new DebugResolutionListener(logger));
                }

                listeners.add(new WarningResolutionListener(logger));

                artifactResolutionResult = artifactCollector.collect(getProjectArtifacts(), project.getArtifact(),
                        managedVersions, localRepo, project.getRemoteArtifactRepositories(),
                        getArtifactMetadataSource(), null, listeners);
            } catch (ArtifactResolutionException e) {
                getLog().debug(e.getMessage(), e);
                getLog().error(Messages.getString("artifactresolution", new Object[] { //$NON-NLS-1$
                        e.getGroupId(), e.getArtifactId(), e.getVersion(), e.getMessage() }));

                // if we are here artifactResolutionResult is null, create a project without dependencies but don't fail
                // (this could be a reactor projects, we don't want to fail everything)
                return new IdeDependency[0];
            }

            // keep track of added reactor projects in order to avoid duplicates
            Set emittedReactorProjectId = new HashSet();

            for (Iterator i = artifactResolutionResult.getArtifactResolutionNodes().iterator(); i.hasNext();) {
                ResolutionNode node = (ResolutionNode) i.next();
                Artifact art = node.getArtifact();
                boolean isReactorProject = getUseProjectReferences() && isAvailableAsAReactorProject(art);

                // don't resolve jars for reactor projects
                if (!isReactorProject) {
                    try {
                        artifactResolver.resolve(art, node.getRemoteRepositories(), localRepository);
                    } catch (ArtifactNotFoundException e) {
                        getLog().debug(e.getMessage(), e);
                        getLog().warn(Messages.getString("artifactdownload", new Object[] { //$NON-NLS-1$
                                e.getGroupId(), e.getArtifactId(), e.getVersion(), e.getMessage() }));
                    } catch (ArtifactResolutionException e) {
                        getLog().debug(e.getMessage(), e);
                        getLog().warn(Messages.getString("artifactresolution", new Object[] { //$NON-NLS-1$
                                e.getGroupId(), e.getArtifactId(), e.getVersion(), e.getMessage() }));
                    }
                }

                if (!isReactorProject
                        || emittedReactorProjectId.add(art.getGroupId() + '-' + art.getArtifactId())) {

                    IdeDependency dep = new IdeDependency(art, isReactorProject);
                    dep = resolveWorkspaceProject(dep);
                    dependencyList.add(dep);

                }

            }

            // @todo a final report with the list of missingArtifacts?

        }

        IdeDependency[] deps = (IdeDependency[]) dependencyList.toArray(new IdeDependency[dependencyList.size()]);

        return deps;
    }

    /**
     * Returns the list of project artifacts. Also artifacts generated from referenced projects will be added, but with
     * the <code>resolved</code> property set to true.
     *
     * @return list of projects artifacts
     * @throws MojoExecutionException if unable to parse dependency versions
     */
    private Set getProjectArtifacts() throws MojoExecutionException {
        // keep it sorted, this should avoid random classpath order in tests
        Set artifacts = new TreeSet();

        for (Iterator dependencies = getProject().getDependencies().iterator(); dependencies.hasNext();) {
            Dependency dependency = (Dependency) dependencies.next();

            String groupId = dependency.getGroupId();
            String artifactId = dependency.getArtifactId();
            VersionRange versionRange;
            try {
                versionRange = VersionRange.createFromVersionSpec(dependency.getVersion());
            } catch (InvalidVersionSpecificationException e) {
                throw new MojoExecutionException(Messages.getString("unabletoparseversion", new Object[] { //$NON-NLS-1$
                        dependency.getArtifactId(), dependency.getVersion(), dependency.getManagementKey(),
                        e.getMessage() }), e);
            }

            String type = dependency.getType();
            if (type == null) {
                type = "jar"; //$NON-NLS-1$
            }
            String classifier = dependency.getClassifier();
            boolean optional = dependency.isOptional();
            String scope = dependency.getScope();
            if (scope == null) {
                scope = Artifact.SCOPE_COMPILE;
            }

            Artifact art = getArtifactFactory().createDependencyArtifact(groupId, artifactId, versionRange, type,
                    classifier, scope, optional);

            if (scope.equalsIgnoreCase(Artifact.SCOPE_SYSTEM)) {
                art.setFile(new File(dependency.getSystemPath()));
            }

            List exclusions = new ArrayList();
            for (Iterator j = dependency.getExclusions().iterator(); j.hasNext();) {
                Exclusion e = (Exclusion) j.next();
                exclusions.add(e.getGroupId() + ":" + e.getArtifactId()); //$NON-NLS-1$
            }

            ArtifactFilter newFilter = new ExcludesArtifactFilter(exclusions);

            art.setDependencyFilter(newFilter);

            artifacts.add(art);
        }

        return artifacts;
    }

    /**
     * Utility method that locates a project producing the given artifact.
     *
     * @param artifact the artifact a project should produce.
     * @return <code>true</code> if the artifact is produced by a reactor projectart.
     */
    private boolean isAvailableAsAReactorProject(Artifact artifact) {
        if (reactorProjects != null) {
            for (Iterator iter = reactorProjects.iterator(); iter.hasNext();) {
                MavenProject reactorProject = (MavenProject) iter.next();

                if (reactorProject.getGroupId().equals(artifact.getGroupId())
                        && reactorProject.getArtifactId().equals(artifact.getArtifactId())) {
                    if (reactorProject.getVersion().equals(artifact.getVersion())) {
                        return true;
                    } else {
                        getLog().info("Artifact " + artifact.getId()
                                + " already available as a reactor project, but with different version. Expected: "
                                + artifact.getVersion() + ", found: " + reactorProject.getVersion());
                    }
                }
            }
        }
        return false;
    }

    private Map createManagedVersionMap(String projectId, DependencyManagement dependencyManagement)
            throws MojoExecutionException {
        Map map;
        if (dependencyManagement != null && dependencyManagement.getDependencies() != null) {
            map = new HashMap();
            for (Iterator i = dependencyManagement.getDependencies().iterator(); i.hasNext();) {
                Dependency d = (Dependency) i.next();

                try {
                    VersionRange versionRange = VersionRange.createFromVersionSpec(d.getVersion());
                    Artifact artifact = artifactFactory.createDependencyArtifact(d.getGroupId(), d.getArtifactId(),
                            versionRange, d.getType(), d.getClassifier(), d.getScope(), d.isOptional());
                    map.put(d.getManagementKey(), artifact);
                } catch (InvalidVersionSpecificationException e) {
                    throw new MojoExecutionException(Messages.getString("unabletoparseversion", new Object[] { //$NON-NLS-1$
                            projectId, d.getVersion(), d.getManagementKey(), e.getMessage() }), e);
                }
            }
        } else {
            map = Collections.EMPTY_MAP;
        }
        return map;
    }

    /**
     * @return the workspaceProjects
     */
    public List getWorkspaceProjects() {
        return workspaceProjects;
    }

    public IdeDependency resolveWorkspaceProject(IdeDependency dep) {
        if (workspaceProjects.contains(dep)) {
            IdeDependency workspaceProject = (IdeDependency) workspaceProjects.get(workspaceProjects.indexOf(dep));
            dep.setIdeProjectName(workspaceProject.getIdeProjectName());
            dep.setOutputDirectory(workspaceProject.getOutputDirectory());
        }
        return dep;
    }

}