001: package org.andromda.maven.plugin.andromdapp;
002:
003: import java.io.File;
004: import java.util.ArrayList;
005: import java.util.Arrays;
006: import java.util.Iterator;
007: import java.util.LinkedHashSet;
008: import java.util.List;
009: import java.util.ListIterator;
010: import java.util.Set;
011: import java.util.Map;
012: import java.util.LinkedHashMap;
013:
014: import org.andromda.core.common.ResourceUtils;
015: import org.andromda.maven.plugin.andromdapp.eclipse.ClasspathWriter;
016: import org.andromda.maven.plugin.andromdapp.eclipse.ProjectWriter;
017: import org.andromda.maven.plugin.andromdapp.utils.ProjectUtils;
018: import org.apache.commons.lang.ObjectUtils;
019: import org.apache.maven.artifact.factory.ArtifactFactory;
020: import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
021: import org.apache.maven.artifact.repository.ArtifactRepository;
022: import org.apache.maven.artifact.resolver.ArtifactResolver;
023: import org.apache.maven.execution.MavenSession;
024: import org.apache.maven.model.Build;
025: import org.apache.maven.model.Plugin;
026: import org.apache.maven.model.PluginExecution;
027: import org.apache.maven.model.PluginManagement;
028: import org.apache.maven.plugin.AbstractMojo;
029: import org.apache.maven.plugin.MojoExecutionException;
030: import org.apache.maven.profiles.DefaultProfileManager;
031: import org.apache.maven.project.MavenProject;
032: import org.apache.maven.project.MavenProjectBuilder;
033: import org.apache.maven.project.ProjectBuildingException;
034: import org.codehaus.plexus.util.DirectoryScanner;
035: import org.codehaus.plexus.util.xml.Xpp3Dom;
036:
037: /**
038: * Writes the necessary .classpath and .project files
039: * for a new eclipse application.
040: *
041: * @goal eclipse
042: * @phase generate-sources
043: * @author Chad Brandon
044: */
045: public class EclipseMojo extends AbstractMojo {
046: /**
047: * @parameter expression="${session}"
048: */
049: private MavenSession session;
050:
051: /**
052: * @parameter expression="${project}"
053: * @required
054: * @readonly
055: */
056: private MavenProject project;
057:
058: /**
059: * Defines the POMs to include when generating the eclipse files.
060: *
061: * @parameter
062: */
063: private String[] includes = new String[] { "*/**/pom.xml" };
064:
065: /**
066: * Defines the POMs to exclude when generating the eclipse files.
067: *
068: * @parameter
069: */
070: private String[] excludes = new String[0];
071:
072: /**
073: * Used to contruct Maven project instances from POMs.
074: *
075: * @component
076: */
077: private MavenProjectBuilder projectBuilder;
078:
079: /**
080: * The name of the variable that will store the maven repository location.
081: *
082: * @parameter expression="${repository.variable.name}
083: */
084: private String repositoryVariableName = "M2_REPO";
085:
086: /**
087: * Artifact factory, needed to download source jars for inclusion in classpath.
088: *
089: * @component role="org.apache.maven.artifact.factory.ArtifactFactory"
090: * @required
091: * @readonly
092: */
093: private ArtifactFactory artifactFactory;
094:
095: /**
096: * Artifact resolver, needed to download source jars for inclusion in classpath.
097: *
098: * @component role="org.apache.maven.artifact.resolver.ArtifactResolver"
099: * @required
100: * @readonly
101: */
102: private ArtifactResolver artifactResolver;
103:
104: /**
105: * @parameter expression="${localRepository}"
106: * @required
107: * @readonly
108: */
109: protected ArtifactRepository localRepository;
110:
111: /**
112: * @component
113: */
114: private ArtifactMetadataSource artifactMetadataSource;
115:
116: /**
117: * The artifact types which should be included in the generated Eclipse classpath.
118: *
119: * @parameter
120: */
121: private Set classpathArtifactTypes = new LinkedHashSet(Arrays
122: .asList(new String[] { "jar" }));
123:
124: /**
125: * Whether or not transitive dependencies shall be included in any resources (i.e. .classpath
126: * that are generated by this mojo).
127: *
128: * @parameter expression="${resolveTransitiveDependencies}"
129: */
130: private boolean resolveTransitiveDependencies = true;
131:
132: /**
133: * Allows non-generated configuration to be "merged" into the generated .classpath file.
134: *
135: * @parameter
136: */
137: private String classpathMerge;
138:
139: /**
140: * @see org.apache.maven.plugin.Mojo#execute()
141: */
142: public void execute() throws MojoExecutionException {
143: try {
144: final MavenProject rootProject = this .getRootProject();
145: final ProjectWriter projectWriter = new ProjectWriter(
146: rootProject, this .getLog());
147: projectWriter.write();
148: final Map originalCompileSourceRoots = this
149: .collectProjectCompileSourceRoots();
150: final List projects = this .collectProjects();
151: this .processCompileSourceRoots(projects);
152: final ClasspathWriter classpathWriter = new ClasspathWriter(
153: rootProject, this .getLog());
154: classpathWriter.write(projects,
155: this .repositoryVariableName, this .artifactFactory,
156: this .artifactResolver, this .localRepository,
157: this .artifactMetadataSource,
158: this .classpathArtifactTypes, this .project
159: .getRemoteArtifactRepositories(),
160: this .resolveTransitiveDependencies,
161: this .classpathMerge);
162: // - reset to the original source roots
163: for (final Iterator iterator = projects.iterator(); iterator
164: .hasNext();) {
165: final MavenProject project = (MavenProject) iterator
166: .next();
167: project.getCompileSourceRoots().clear();
168: project.getCompileSourceRoots().addAll(
169: (List) originalCompileSourceRoots.get(project));
170: }
171: } catch (Throwable throwable) {
172: throw new MojoExecutionException(
173: "Error creating eclipse configuration", throwable);
174: }
175: }
176:
177: /**
178: * Collects all existing project compile source roots.
179: *
180: * @return a collection of collections
181: */
182: private Map collectProjectCompileSourceRoots() throws Exception {
183: final Map sourceRoots = new LinkedHashMap();
184: for (final Iterator iterator = this .collectProjects()
185: .iterator(); iterator.hasNext();) {
186: final MavenProject project = (MavenProject) iterator.next();
187: sourceRoots.put(project, new ArrayList(project
188: .getCompileSourceRoots()));
189: }
190: return sourceRoots;
191: }
192:
193: private List projects = new ArrayList();
194:
195: /**
196: * Collects all projects from all POMs within the current project.
197: *
198: * @return all applicable Maven project instances.
199: *
200: * @throws MojoExecutionException
201: */
202: private List collectProjects() throws Exception {
203: if (projects.isEmpty()) {
204: final List poms = this .getPoms();
205: for (ListIterator iterator = poms.listIterator(); iterator
206: .hasNext();) {
207: final File pom = (File) iterator.next();
208: try {
209: // - first attempt to get the existing project from the session
210: MavenProject project = ProjectUtils.getProject(
211: this .projectBuilder, this .session, pom);
212: if (project == null) {
213: // - if we didn't find it in the session, create it
214: project = this .projectBuilder.build(pom,
215: this .session.getLocalRepository(),
216: new DefaultProfileManager(this .session
217: .getContainer()));
218: }
219: this .getLog().info(
220: "found project " + project.getId());
221: projects.add(project);
222: } catch (ProjectBuildingException exception) {
223: throw new MojoExecutionException("Error loading "
224: + pom, exception);
225: }
226: }
227: }
228: return projects;
229: }
230:
231: /**
232: * Processes the project compile source roots (adds all appropriate ones to the projects)
233: * so that they're avialable to the eclipse mojos.
234: *
235: * @param projects the projects to process.
236: * @return the source roots.
237: * @throws Exception
238: */
239: private void processCompileSourceRoots(final List projects)
240: throws Exception {
241: for (final Iterator iterator = projects.iterator(); iterator
242: .hasNext();) {
243: final MavenProject project = (MavenProject) iterator.next();
244: final Set compileSourceRoots = new LinkedHashSet(project
245: .getCompileSourceRoots());
246: compileSourceRoots.addAll(this
247: .getExtraSourceDirectories(project));
248: final String testSourceDirectory = project.getBuild()
249: .getTestSourceDirectory();
250: if (testSourceDirectory != null
251: && testSourceDirectory.trim().length() > 0) {
252: compileSourceRoots.add(testSourceDirectory);
253: }
254: project.getCompileSourceRoots().clear();
255: project.getCompileSourceRoots().addAll(compileSourceRoots);
256: }
257: }
258:
259: /**
260: * The artifact id for the multi source plugin.
261: */
262: private static final String MULTI_SOURCE_PLUGIN_ARTIFACT_ID = "andromda-multi-source-plugin";
263:
264: /**
265: * Retrieves any additional source directories which are defined within the andromda-multi-source-plugin.
266: *
267: * @param project the maven project from which to retrieve the extra source directories.
268: * @return the list of extra source directories.
269: */
270: private List getExtraSourceDirectories(final MavenProject project) {
271: final List sourceDirectories = new ArrayList();
272: final Build build = project.getBuild();
273: if (build != null) {
274: final PluginManagement pluginManagement = build
275: .getPluginManagement();
276: if (pluginManagement != null
277: && !pluginManagement.getPlugins().isEmpty()) {
278: Plugin multiSourcePlugin = null;
279: for (final Iterator iterator = pluginManagement
280: .getPlugins().iterator(); iterator.hasNext();) {
281: final Plugin plugin = (Plugin) iterator.next();
282: if (MULTI_SOURCE_PLUGIN_ARTIFACT_ID.equals(plugin
283: .getArtifactId())) {
284: multiSourcePlugin = plugin;
285: break;
286: }
287: }
288: final Xpp3Dom configuration = this
289: .getConfiguration(multiSourcePlugin);
290: if (configuration != null
291: && configuration.getChildCount() > 0) {
292: final Xpp3Dom directories = configuration
293: .getChild(0);
294: if (directories != null) {
295: final int childCount = directories
296: .getChildCount();
297: if (childCount > 0) {
298: final String baseDirectory = ResourceUtils
299: .normalizePath(ObjectUtils
300: .toString(project
301: .getBasedir()) + '/');
302: final Xpp3Dom[] children = directories
303: .getChildren();
304: for (int ctr = 0; ctr < childCount; ctr++) {
305: final Xpp3Dom child = children[ctr];
306: if (child != null) {
307: String directoryValue = ResourceUtils
308: .normalizePath(child
309: .getValue());
310: if (directoryValue != null) {
311: if (!directoryValue
312: .startsWith(baseDirectory)) {
313: directoryValue = ResourceUtils
314: .normalizePath(baseDirectory
315: + directoryValue
316: .trim());
317: }
318: sourceDirectories
319: .add(directoryValue);
320: }
321: }
322: }
323: }
324: }
325: }
326: }
327: }
328: return sourceDirectories;
329: }
330:
331: /**
332: * Retrieves the appropriate configuration instance (first tries
333: * to get the configuration from the plugin, then tries from the plugin's
334: * executions.
335: *
336: * @param plugin the plugin from which the retrieve the configuration.
337: * @return the plugin's configuration, or null if not found.
338: */
339: private Xpp3Dom getConfiguration(final Plugin plugin) {
340: Xpp3Dom configuration = null;
341: if (plugin != null) {
342: if (plugin.getConfiguration() != null) {
343: configuration = (Xpp3Dom) plugin.getConfiguration();
344: } else {
345: final List executions = plugin.getExecutions();
346: if (executions != null && !executions.isEmpty()) {
347: // - there should only be one execution so we get the first one
348: final PluginExecution execution = (PluginExecution) plugin
349: .getExecutions().iterator().next();
350: configuration = (Xpp3Dom) execution
351: .getConfiguration();
352: }
353: }
354: }
355: return configuration;
356: }
357:
358: /**
359: * Stores the root project.
360: */
361: private MavenProject rootProject;
362:
363: /**
364: * Retrieves the root project (i.e. the root parent project)
365: * for this project.
366: *
367: * @return the root project.
368: * @throws MojoExecutionException
369: */
370: private MavenProject getRootProject() throws MojoExecutionException {
371: if (this .rootProject == null) {
372: final MavenProject firstParent = this .project.getParent();
373: if (firstParent != null) {
374: for (this .rootProject = firstParent; this .rootProject
375: .getParent() != null; this .rootProject = this .rootProject
376: .getParent()) {
377: ;
378: }
379: } else {
380: this .rootProject = this .project;
381: }
382: }
383: return this .rootProject;
384: }
385:
386: /**
387: * Retrieves all the POMs for the given project.
388: *
389: * @return all poms found.
390: * @throws MojoExecutionException
391: */
392: private List getPoms() throws MojoExecutionException {
393: final DirectoryScanner scanner = new DirectoryScanner();
394: scanner.setBasedir(this .getRootProject().getBasedir());
395: scanner.setIncludes(this .includes);
396: scanner.setExcludes(this .excludes);
397: scanner.scan();
398:
399: List poms = new ArrayList();
400:
401: for (int ctr = 0; ctr < scanner.getIncludedFiles().length; ctr++) {
402: final File file = new File(this.getRootProject()
403: .getBasedir(), scanner.getIncludedFiles()[ctr]);
404: if (file.exists()) {
405: poms.add(file);
406: }
407: }
408:
409: return poms;
410: }
411: }
|