001package org.andromda.maven.plugin.andromdapp;
002
003import java.io.File;
004import java.net.MalformedURLException;
005import java.net.URL;
006import java.net.URLClassLoader;
007import java.util.ArrayList;
008import java.util.Collection;
009import java.util.LinkedHashSet;
010import java.util.List;
011import java.util.Set;
012import org.andromda.core.common.ClassUtils;
013import org.andromda.maven.plugin.andromdapp.script.ScriptClassGenerator;
014import org.apache.maven.artifact.Artifact;
015import org.apache.maven.artifact.factory.ArtifactFactory;
016import org.apache.maven.artifact.repository.ArtifactRepository;
017import org.apache.maven.model.Dependency;
018import org.apache.maven.plugin.AbstractMojo;
019import org.apache.maven.plugin.MojoExecutionException;
020import org.apache.maven.plugin.MojoFailureException;
021import org.apache.maven.project.MavenProject;
022
023/**
024 * Allows for the {@link ScriptClassGenerator} mojo to be invoked.
025 * on one or more given classes.
026 *
027 * @author Chad Brandon
028 * @goal instrument-scripts
029 * @phase compile
030 * @requiresDependencyResolution
031 */
032public class ScriptClassGeneratorMojo
033    extends AbstractMojo
034{
035    /**
036     * Defines the java files who's classes will be instrumented.
037     *
038     * @required
039     * @parameter
040     */
041    private Location[] locations;
042
043    /**
044     * Defines the fully qualified class name of the script wrapper implementation.
045     *
046     * @parameter
047     * @required
048     */
049    private String scriptWrapper;
050
051    /**
052     * @parameter expression="${project}"
053     * @required
054     * @readonly
055     */
056    private MavenProject project;
057
058    /**
059     * @component role="org.apache.maven.artifact.factory.ArtifactFactory"
060     * @required
061     * @readonly
062     */
063    protected ArtifactFactory factory;
064
065    /**
066     * @parameter expression="${localRepository}"
067     * @required
068     * @readonly
069     */
070    protected ArtifactRepository localRepository;
071
072    /**
073     * The java file extension
074     */
075    private static final String JAVA_EXTENSION = ".java";
076
077    /**
078     * @see org.apache.maven.plugin.Mojo#execute()
079     */
080    public void execute()
081        throws MojoExecutionException, MojoFailureException
082    {
083        try
084        {
085            final ScriptClassGenerator generator = ScriptClassGenerator.getInstance(this.scriptWrapper);
086            if (this.locations != null)
087            {
088                final List<String> classpathElements = new ArrayList<String>(this.getProvidedClasspathElements());
089                classpathElements.addAll(this.project.getRuntimeClasspathElements());
090                this.initializeClassLoader(classpathElements);
091                for (int ctr = 0; ctr < locations.length; ctr++)
092                {
093                    final Location location = locations[ctr];
094                    String rootPath = location.getRootPath();
095                    for (final String path : location.getPaths())
096                    {
097                        final int extensionIndex = path.lastIndexOf(JAVA_EXTENSION);
098                        if (extensionIndex != -1)
099                        {
100                            final String className = path.substring(
101                                    0,
102                                    extensionIndex).replaceAll(
103                                    "\\\\|/",
104                                    "\\.");
105                            this.getLog().info("injecting script wrapper: " + className);
106                            generator.modifyClass(
107                                rootPath,
108                                ClassUtils.loadClass(className));
109                        }
110                    }
111                }
112            }
113        }
114        catch (final Throwable throwable)
115        {
116            throw new MojoExecutionException("Failed to inject script wrappers", throwable);
117        }
118    }
119
120    /**
121     * Adds any dependencies to the current project from the plugin
122     * having the given <code>pluginArtifactId</code>.
123     * This project artifact dependencies are added.
124     * scope=PROVIDED the artifact scope in which to add them (runtime, compile, etc).
125     * @return classpathElements
126     */
127    protected List<String> getProvidedClasspathElements()
128    {
129        final List<String> classpathElements = new ArrayList<String>();
130        for (final Object dependency : this.project.getDependencies())
131        {
132            final Artifact artifact = this.getArtifact(
133                (Dependency)dependency,
134                Artifact.SCOPE_PROVIDED);
135            if (artifact != null)
136            {
137                classpathElements.add(artifact.getFile().getAbsolutePath());
138            }
139        }
140        return classpathElements;
141    }
142
143    /**
144     * Adds a dependency to the current project's dependencies.
145     *
146     * @param dependency
147     * @param scope the scope of the artifact
148     */
149    private Artifact getArtifact(
150        final Dependency dependency,
151        final String scope)
152    {
153        Artifact artifact = null;
154        final ArtifactRepository localRepository = this.localRepository;
155        final MavenProject project = this.project;
156        if (project != null && localRepository != null)
157        {
158            if (dependency != null)
159            {
160                artifact =
161                    this.factory.createArtifact(
162                        dependency.getGroupId(),
163                        dependency.getArtifactId(),
164                        dependency.getVersion(),
165                        scope,
166                        dependency.getType());
167                final File file = new File(
168                        localRepository.getBasedir(),
169                        localRepository.pathOf(artifact));
170                artifact.setFile(file);
171            }
172        }
173        return artifact;
174    }
175
176    /**
177     * Sets the current context class loader from the given runtime classpath elements.
178     * @param classpathFiles
179     * @throws MalformedURLException
180     */
181    protected void initializeClassLoader(final Collection<String> classpathFiles)
182        throws MalformedURLException
183    {
184        final Set<URL> classpathUrls = new LinkedHashSet<URL>();
185        classpathUrls.add(new File(this.project.getBuild().getOutputDirectory()).toURI().toURL());
186        if (classpathFiles != null)
187        {
188            for (String fileName : classpathFiles)
189            {
190                final File file = new File(fileName);
191                if (this.getLog().isDebugEnabled())
192                {
193                    getLog().debug("adding to classpath '" + file + '\'');
194                }
195                classpathUrls.add(file.toURI().toURL());
196            }
197        }
198        final URLClassLoader loader =
199            new URLClassLoader(classpathUrls.toArray(new URL[classpathUrls.size()]),
200                Thread.currentThread().getContextClassLoader());
201        Thread.currentThread().setContextClassLoader(loader);
202    }
203}