View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.plugin.eclipse;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.Iterator;
26  import java.util.LinkedHashSet;
27  import java.util.LinkedList;
28  import java.util.List;
29  import java.util.ListIterator;
30  import java.util.Map;
31  import java.util.Set;
32  import java.util.TreeSet;
33  
34  import org.apache.maven.artifact.Artifact;
35  import org.apache.maven.artifact.handler.ArtifactHandler;
36  import org.apache.maven.model.Resource;
37  import org.apache.maven.plugin.MojoExecutionException;
38  import org.apache.maven.plugin.eclipse.writers.EclipseClasspathWriter;
39  import org.apache.maven.plugin.eclipse.writers.EclipseManifestWriter;
40  import org.apache.maven.plugin.eclipse.writers.EclipseOSGiManifestWriter;
41  import org.apache.maven.plugin.eclipse.writers.EclipseProjectWriter;
42  import org.apache.maven.plugin.eclipse.writers.EclipseSettingsWriter;
43  import org.apache.maven.plugin.eclipse.writers.EclipseWriterConfig;
44  import org.apache.maven.plugin.eclipse.writers.wtp.EclipseWtpApplicationXMLWriter;
45  import org.apache.maven.plugin.eclipse.writers.wtp.EclipseWtpComponent15Writer;
46  import org.apache.maven.plugin.eclipse.writers.wtp.EclipseWtpComponentWriter;
47  import org.apache.maven.plugin.eclipse.writers.wtp.EclipseWtpFacetsWriter;
48  import org.apache.maven.plugin.eclipse.writers.wtp.EclipseWtpmodulesWriter;
49  import org.apache.maven.plugin.ide.AbstractIdeSupportMojo;
50  import org.apache.maven.plugin.ide.IdeDependency;
51  import org.apache.maven.plugin.ide.IdeUtils;
52  import org.apache.maven.plugin.ide.JeeUtils;
53  import org.apache.maven.project.MavenProject;
54  import org.codehaus.plexus.util.FileUtils;
55  import org.codehaus.plexus.util.StringUtils;
56  import org.codehaus.plexus.util.xml.Xpp3Dom;
57  
58  /**
59   * Generates the following eclipse configuration files:
60   * <ul>
61   * <li><code>.project</code> and <code>.classpath</code> files</li>
62   * <li><code>.setting/org.eclipse.jdt.core.prefs</code> with project specific compiler settings</li>
63   * <li>various configuration files for WTP (Web Tools Project), if the parameter <code>wtpversion</code> is set to a
64   * valid version (WTP configuration is not generated by default)</li>
65   * </ul>
66   * If this goal is run on a multiproject root, dependencies between modules will be configured as direct project
67   * dependencies in Eclipse (unless <code>useProjectReferences</code> is set to <code>false</code>).
68   * 
69   * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
70   * @author <a href="mailto:fgiust@apache.org">Fabrizio Giustina</a>
71   * @version $Id: EclipsePlugin.java 599461 2007-11-29 14:43:17Z aheritier $
72   * @goal eclipse
73   * @execute phase="generate-resources"
74   */
75  public class EclipsePlugin
76      extends AbstractIdeSupportMojo
77  {
78  
79      private static final String NATURE_WST_FACET_CORE_NATURE = "org.eclipse.wst.common.project.facet.core.nature"; //$NON-NLS-1$
80  
81      private static final String BUILDER_WST_COMPONENT_STRUCTURAL_DEPENDENCY_RESOLVER =
82          "org.eclipse.wst.common.modulecore.ComponentStructuralBuilderDependencyResolver"; //$NON-NLS-1$
83  
84      protected static final String BUILDER_WST_VALIDATION = "org.eclipse.wst.validation.validationbuilder"; //$NON-NLS-1$
85  
86      private static final String BUILDER_JDT_CORE_JAVA = "org.eclipse.jdt.core.javabuilder"; //$NON-NLS-1$
87  
88      private static final String BUILDER_WST_COMPONENT_STRUCTURAL =
89          "org.eclipse.wst.common.modulecore.ComponentStructuralBuilder"; //$NON-NLS-1$
90  
91      private static final String BUILDER_WST_FACET = "org.eclipse.wst.common.project.facet.core.builder"; //$NON-NLS-1$
92  
93      private static final String BUILDER_PDE_MANIFEST = "org.eclipse.pde.ManifestBuilder"; //$NON-NLS-1$
94  
95      private static final String BUILDER_PDE_SCHEMA = "org.eclipse.pde.SchemaBuilder"; //$NON-NLS-1$
96  
97      private static final String NATURE_WST_MODULE_CORE_NATURE = "org.eclipse.wst.common.modulecore.ModuleCoreNature"; //$NON-NLS-1$
98  
99      private static final String NATURE_JDT_CORE_JAVA = "org.eclipse.jdt.core.javanature"; //$NON-NLS-1$
100 
101     private static final String NATURE_JEM_WORKBENCH_JAVA_EMF = "org.eclipse.jem.workbench.JavaEMFNature"; //$NON-NLS-1$
102 
103     private static final String NATURE_PDE_PLUGIN = "org.eclipse.pde.PluginNature"; //$NON-NLS-1$
104 
105     protected static final String COMMON_PATH_JDT_LAUNCHING_JRE_CONTAINER = "org.eclipse.jdt.launching.JRE_CONTAINER"; //$NON-NLS-1$
106 
107     protected static final String REQUIRED_PLUGINS_CONTAINER = "org.eclipse.pde.core.requiredPlugins"; //$NON-NLS-1$
108 
109     // warning, order is important for binary search
110     public static final String[] WTP_SUPPORTED_VERSIONS = new String[] { "1.0", "1.5", "2.0", "R7", "none" }; //$NON-NLS-1$ //$NON-NLS-2$  //$NON-NLS-3$
111 
112     /**
113      * Constant for 'artifactId' element in POM.xml.
114      */
115     private static final String POM_ELT_ARTIFACT_ID = "artifactId"; //$NON-NLS-1$
116 
117     /**
118      * Constant for 'groupId' element in POM.xml.
119      */
120     private static final String POM_ELT_GROUP_ID = "groupId"; //$NON-NLS-1$
121 
122     /**
123      * List of eclipse project natures. By default the <code>org.eclipse.jdt.core.javanature</code> nature plus the
124      * needed WTP natures are added. Natures added using this property <strong>replace</strong> the default list.
125      * 
126      * <pre>
127      * &lt;projectnatures&gt;
128      *    &lt;projectnature&gt;org.eclipse.jdt.core.javanature&lt;/projectnature&gt;
129      *    &lt;projectnature&gt;org.eclipse.wst.common.modulecore.ModuleCoreNature&lt;/projectnature&gt;
130      * &lt;/projectnatures&gt;
131      * </pre>
132      * 
133      * @parameter
134      */
135     private List projectnatures;
136 
137     /**
138      * List of eclipse project natures to be added to the default ones.
139      * 
140      * <pre>
141      * &lt;additionalProjectnatures&gt;
142      *    &lt;projectnature&gt;org.springframework.ide.eclipse.core.springnature&lt;/projectnature&gt;
143      * &lt;/additionalProjectnatures&gt;
144      * </pre>
145      * 
146      * @parameter
147      */
148     private List additionalProjectnatures;
149 
150     /**
151      * List of eclipse project facets to be added to the default ones.
152      * 
153      * <pre>
154      * &lt;additionalProjectFacets&gt;
155      *    &lt;jst.jsf&gt;1.1&lt;jst.jsf/&gt;
156      * &lt;/additionalProjectFacets&gt;
157      * </pre>
158      * 
159      * @parameter
160      */
161     private Map additionalProjectFacets;
162 
163     /**
164      * List of eclipse build commands. By default the <code>org.eclipse.jdt.core.javabuilder</code> builder plus the
165      * needed WTP builders are added. If you specify any configuration for this parameter, only those buildcommands
166      * specified will be used; the defaults won't be added. Use the <code>additionalBuildCommands</code> parameter for
167      * that. Configuration example: Old style:
168      * 
169      * <pre>
170      * &lt;buildcommands&gt;
171      *    &lt;buildcommand&gt;org.eclipse.wst.common.modulecore.ComponentStructuralBuilder&lt;/buildcommand&gt;
172      *    &lt;buildcommand&gt;org.eclipse.jdt.core.javabuilder&lt;/buildcommand&gt;
173      *    &lt;buildcommand&gt;org.eclipse.wst.common.modulecore.ComponentStructuralBuilderDependencyResolver&lt;/buildcommand&gt;
174      * &lt;/buildcommands&gt;
175      * </pre>
176      * 
177      * For new style, see <code>additionalBuildCommands</code>.
178      * 
179      * @parameter
180      */
181     private List buildcommands;
182 
183     /**
184      * List of eclipse build commands to be added to the default ones. Old style:
185      * 
186      * <pre>
187      * &lt;additionalBuildcommands&gt;
188      *    &lt;buildcommand&gt;org.springframework.ide.eclipse.core.springbuilder&lt;/buildcommand&gt;
189      * &lt;/additionalBuildcommands&gt;
190      * </pre>
191      * 
192      * New style:
193      * 
194      * <pre>
195      * &lt;additionalBuildcommands&gt;
196      *    &lt;buildCommand&gt;
197      *      &lt;name&gt;org.ui.externaltools.ExternalToolBuilder&lt;/name&gt;
198      *      &lt;triggers&gt;auto,full,incremental,&lt;/triggers&gt;
199      *      &lt;arguments&gt;
200      *        &lt;LaunchConfigHandle&gt;&amp;lt;project&amp;gt;./externalToolBuilders/MavenBuilder.launch&lt;/LaunchConfighandle&gt;
201      *      &lt;/arguments&gt;
202      *    &lt;/buildCommand&gt;
203      * &lt;/additionalBuildcommands&gt;
204      * </pre>
205      * 
206      * Note the difference between <code>build<strong>c</strong>ommand</code> and
207      * <code>build<strong>C</strong>ommand</code>. You can mix and match old and new-style configuration entries.
208      * 
209      * @parameter
210      */
211     private List additionalBuildcommands;
212 
213     /**
214      * List of container classpath entries. By default the <code>org.eclipse.jdt.launching.JRE_CONTAINER</code>
215      * classpath container is added. Configuration example:
216      * 
217      * <pre>
218      * &lt;classpathContainers&gt;
219      *    &lt;classpathContainer&gt;org.eclipse.jdt.launching.JRE_CONTAINER&lt;/classpathContainer&gt;
220      *    &lt;classpathContainer&gt;org.eclipse.jst.server.core.container/org.eclipse.jst.server.tomcat.runtimeTarget/Apache Tomcat v5.5&lt;/classpathContainer&gt;
221      *    &lt;classpathContainer&gt;org.eclipse.jst.j2ee.internal.web.container/artifact&lt;/classpathContainer&gt;
222      * &lt;/classpathContainers&gt;
223      * </pre>
224      * 
225      * @parameter
226      */
227     private List classpathContainers;
228 
229     /**
230      * Enables/disables the downloading of source attachments. Defaults to false. DEPRECATED - use downloadSources
231      * 
232      * @parameter expression="${eclipse.downloadSources}"
233      * @deprecated use downloadSources
234      */
235     private boolean eclipseDownloadSources;
236 
237     /**
238      * Eclipse workspace directory.
239      * 
240      * @parameter expression="${eclipse.workspace}" alias="outputDir"
241      */
242     private File eclipseProjectDir;
243 
244     /**
245      * When set to false, the plugin will not create sub-projects and instead reference those sub-projects using the
246      * installed package in the local repository
247      * 
248      * @parameter expression="${eclipse.useProjectReferences}" default-value="true"
249      * @required
250      */
251     private boolean useProjectReferences;
252 
253     /**
254      * The default output directory
255      * 
256      * @parameter expression="${outputDirectory}" alias="outputDirectory"
257      *            default-value="${project.build.outputDirectory}"
258      * @required
259      */
260     private File buildOutputDirectory;
261 
262     /**
263      * The version of WTP for which configuration files will be generated. The default value is "none" (don't generate
264      * WTP configuration), supported versions are "R7", "1.0", and "1.5"
265      * 
266      * @parameter expression="${wtpversion}" default-value="none"
267      */
268     private String wtpversion;
269 
270     /**
271      * JEE context name of the WTP module. ( ex. WEB context name ).
272      * 
273      * @parameter expression="${wtpContextName}"
274      */
275     private String wtpContextName;
276 
277     /**
278      * Is it an PDE project? If yes, the plugin adds the necessary natures and build commands to the .project file.
279      * Additionally it copies all libraries to a project local directory and references them instead of referencing the
280      * files in the local Maven repository. It also ensured that the "Bundle-Classpath" in META-INF/MANIFEST.MF is
281      * synchronized.
282      * 
283      * @parameter expression="${eclipse.pde}" default-value="false"
284      */
285     private boolean pde;
286 
287     /**
288      * The relative path of the manifest file
289      * 
290      * @parameter expression="${eclipse.manifest}" default-value="${basedir}/META-INF/MANIFEST.MF"
291      */
292     private File manifest;
293 
294     /**
295      * Allow to configure additional generic configuration files for eclipse that will be written out to disk when
296      * running eclipse:eclipse. FOr each file you can specify the name and the text content.
297      * 
298      * <pre>
299      * &lt;additionalConfig&gt;
300      *    &lt;file&gt;
301      *      &lt;name&gt;.checkstyle&lt;/name&gt;
302      *      &lt;content&gt;
303      *        &lt;![CDATA[&lt;fileset-config file-format-version=&quot;1.2.0&quot; simple-config=&quot;true&quot;&gt;
304      *          &lt;fileset name=&quot;all&quot; enabled=&quot;true&quot; check-config-name=&quot;acme corporate style&quot; local=&quot;false&quot;&gt;
305      *              &lt;file-match-pattern match-pattern=&quot;.&quot; include-pattern=&quot;true&quot;/&gt;
306      *          &lt;/fileset&gt;
307      *          &lt;filter name=&quot;NonSrcDirs&quot; enabled=&quot;true&quot;/&gt;
308      *        &lt;/fileset-config&gt;]]&gt;
309      *      &lt;/content&gt;
310      *    &lt;/file&gt;
311      * &lt;/additionalConfig&gt;
312      * </pre>
313      * 
314      * @parameter
315      */
316     private EclipseConfigFile[] additionalConfig;
317 
318     /**
319      * If set to <code>true</code>, the version number of the artifact is appended to the name of the generated
320      * Eclipse project. See projectNameTemplate for other options.
321      * 
322      * @parameter expression="${eclipse.addVersionToProjectName}" default-value="false"
323      */
324     private boolean addVersionToProjectName;
325 
326     /**
327      * If set to <code>true</code>, the groupId of the artifact is appended to the name of the generated Eclipse
328      * project. See projectNameTemplate for other options.
329      * 
330      * @parameter expression="${eclipse.addGroupIdToProjectName}" default-value="false"
331      */
332     private boolean addGroupIdToProjectName;
333 
334     /**
335      * Allows configuring the name of the eclipse projects. This property if set wins over addVersionToProjectName and
336      * addGroupIdToProjectName You can use <code>[groupId]</code>, <code>[artifactId]</code> and
337      * <code>[version]</code> variables. eg. <code>[groupId].[artifactId]-[version]</code>
338      * 
339      * @parameter expression="${eclipse.projectNameTemplate}"
340      */
341     private String projectNameTemplate;
342 
343     /**
344      * Parsed wtp version.
345      */
346     private float wtpVersionFloat;
347 
348     /**
349      * Not a plugin parameter. Is this a java project?
350      */
351     private boolean isJavaProject;
352 
353     /**
354      * Must the manifest files be written for java projects so that that the jee classpath for wtp is correct.
355      * 
356      * @parameter expression="${eclipse.wtpmanifest}" default-value="false"
357      */
358     private boolean wtpmanifest;
359 
360     /**
361      * Must the application files be written for ear projects in a separate directory.
362      * 
363      * @parameter expression="${eclipse.wtpapplicationxml}" default-value="false"
364      */
365     private boolean wtpapplicationxml;
366 
367     protected boolean isJavaProject()
368     {
369         return isJavaProject;
370     }
371 
372     protected boolean isPdeProject()
373     {
374         return pde;
375     }
376 
377     /**
378      * Getter for <code>buildcommands</code>.
379      * 
380      * @return Returns the buildcommands.
381      */
382     public List getBuildcommands()
383     {
384         return this.buildcommands;
385     }
386 
387     /**
388      * Setter for <code>buildcommands</code>.
389      * 
390      * @param buildcommands The buildcommands to set.
391      */
392     public void setBuildcommands( List buildcommands )
393     {
394         this.buildcommands = buildcommands;
395     }
396 
397     /**
398      * Getter for <code>buildOutputDirectory</code>.
399      * 
400      * @return Returns the buildOutputDirectory.
401      */
402     public File getBuildOutputDirectory()
403     {
404         return this.buildOutputDirectory;
405     }
406 
407     /**
408      * Setter for <code>buildOutputDirectory</code>.
409      * 
410      * @param buildOutputDirectory The buildOutputDirectory to set.
411      */
412     public void setBuildOutputDirectory( File buildOutputDirectory )
413     {
414         this.buildOutputDirectory = buildOutputDirectory;
415     }
416 
417     /**
418      * Getter for <code>classpathContainers</code>.
419      * 
420      * @return Returns the classpathContainers.
421      */
422     public List getClasspathContainers()
423     {
424         return this.classpathContainers;
425     }
426 
427     /**
428      * Setter for <code>classpathContainers</code>.
429      * 
430      * @param classpathContainers The classpathContainers to set.
431      */
432     public void setClasspathContainers( List classpathContainers )
433     {
434         this.classpathContainers = classpathContainers;
435     }
436 
437     /**
438      * Getter for <code>eclipseProjectDir</code>.
439      * 
440      * @return Returns the eclipseProjectDir.
441      */
442     public File getEclipseProjectDir()
443     {
444         return this.eclipseProjectDir;
445     }
446 
447     /**
448      * Setter for <code>eclipseProjectDir</code>.
449      * 
450      * @param eclipseProjectDir The eclipseProjectDir to set.
451      */
452     public void setEclipseProjectDir( File eclipseProjectDir )
453     {
454         this.eclipseProjectDir = eclipseProjectDir;
455     }
456 
457     /**
458      * Getter for <code>projectnatures</code>.
459      * 
460      * @return Returns the projectnatures.
461      */
462     public List getProjectnatures()
463     {
464         return this.projectnatures;
465     }
466 
467     /**
468      * Setter for <code>projectnatures</code>.
469      * 
470      * @param projectnatures The projectnatures to set.
471      */
472     public void setProjectnatures( List projectnatures )
473     {
474         this.projectnatures = projectnatures;
475     }
476 
477     /**
478      * Getter for <code>useProjectReferences</code>.
479      * 
480      * @return Returns the useProjectReferences.
481      */
482     public boolean getUseProjectReferences()
483     {
484         return this.useProjectReferences;
485     }
486 
487     /**
488      * Setter for <code>useProjectReferences</code>.
489      * 
490      * @param useProjectReferences The useProjectReferences to set.
491      */
492     public void setUseProjectReferences( boolean useProjectReferences )
493     {
494         this.useProjectReferences = useProjectReferences;
495     }
496 
497     /**
498      * Getter for <code>wtpversion</code>.
499      * 
500      * @return Returns the wtpversion.
501      */
502     public String getWtpversion()
503     {
504         return this.wtpversion;
505     }
506 
507     /**
508      * Setter for <code>wtpversion</code>.
509      * 
510      * @param wtpversion The wtpversion to set.
511      */
512     public void setWtpversion( String wtpversion )
513     {
514         this.wtpversion = wtpversion;
515     }
516 
517     /**
518      * Getter for <code>additionalBuildcommands</code>.
519      * 
520      * @return Returns the additionalBuildcommands.
521      */
522     public List getAdditionalBuildcommands()
523     {
524         return this.additionalBuildcommands;
525     }
526 
527     /**
528      * Setter for <code>additionalBuildcommands</code>.
529      * 
530      * @param additionalBuildcommands The additionalBuildcommands to set.
531      */
532     public void setAdditionalBuildcommands( List additionalBuildcommands )
533     {
534         this.additionalBuildcommands = additionalBuildcommands;
535     }
536 
537     /**
538      * Getter for <code>additionalProjectnatures</code>.
539      * 
540      * @return Returns the additionalProjectnatures.
541      */
542     public List getAdditionalProjectnatures()
543     {
544         return this.additionalProjectnatures;
545     }
546 
547     /**
548      * Setter for <code>additionalProjectnatures</code>.
549      * 
550      * @param additionalProjectnatures The additionalProjectnatures to set.
551      */
552     public void setAdditionalProjectnatures( List additionalProjectnatures )
553     {
554         this.additionalProjectnatures = additionalProjectnatures;
555     }
556 
557     /**
558      * Getter for <code>addVersionToProjectName</code>.
559      */
560     public boolean isAddVersionToProjectName()
561     {
562         return addVersionToProjectName;
563     }
564 
565     /**
566      * Setter for <code>addVersionToProjectName</code>.
567      */
568     public void setAddVersionToProjectName( boolean addVersionToProjectName )
569     {
570         this.addVersionToProjectName = addVersionToProjectName;
571     }
572 
573     /**
574      * Getter for <code>addGroupIdToProjectName</code>.
575      */
576     public boolean isAddGroupIdToProjectName()
577     {
578         return addGroupIdToProjectName;
579     }
580 
581     /**
582      * Setter for <code>addGroupIdToProjectName</code>.
583      */
584     public void setAddGroupIdToProjectName( boolean addGroupIdToProjectName )
585     {
586         this.addGroupIdToProjectName = addGroupIdToProjectName;
587     }
588 
589     public String getProjectNameTemplate()
590     {
591         return projectNameTemplate;
592     }
593 
594     public void setProjectNameTemplate( String projectNameTemplate )
595     {
596         this.projectNameTemplate = projectNameTemplate;
597     }
598 
599     /**
600      * @see org.apache.maven.plugin.Mojo#execute()
601      */
602     public boolean setup()
603         throws MojoExecutionException
604     {
605         boolean ready = true;
606 
607         checkDeprecations();
608 
609         ready = validate();
610 
611         // TODO: Why are we using project in some places, and executedProject in others??
612         ArtifactHandler artifactHandler = this.project.getArtifact().getArtifactHandler();
613 
614         // ear projects don't contain java sources
615         // pde projects are always java projects
616         isJavaProject =
617             pde ||
618                 ( Constants.LANGUAGE_JAVA.equals( artifactHandler.getLanguage() ) && !Constants.PROJECT_PACKAGING_EAR.equals( packaging ) );
619 
620         setupExtras();
621 
622         parseConfigurationOptions();
623 
624         // defaults
625         if ( projectnatures == null )
626         {
627             fillDefaultNatures( packaging );
628         }
629 
630         if ( additionalProjectnatures != null )
631         {
632             projectnatures.addAll( additionalProjectnatures );
633         }
634 
635         if ( buildcommands == null )
636         {
637             fillDefaultBuilders( packaging );
638         }
639         else
640         {
641             convertBuildCommandList( buildcommands );
642         }
643 
644         if ( additionalBuildcommands != null )
645         {
646             convertBuildCommandList( additionalBuildcommands );
647             buildcommands.addAll( additionalBuildcommands );
648         }
649 
650         if ( classpathContainers == null )
651         {
652             fillDefaultClasspathContainers( packaging );
653         }
654         else
655         {
656             verifyClasspathContainerListIsComplete();
657         }
658 
659         // ready to start
660         return ready;
661     }
662 
663     protected void convertBuildCommandList( List commands )
664     {
665         if ( commands != null )
666         {
667             for ( ListIterator i = commands.listIterator(); i.hasNext(); )
668             {
669                 Object command = i.next();
670 
671                 if ( command instanceof String )
672                 {
673                     command = new BuildCommand( (String) command );
674                     i.set( command );
675                 }
676             }
677         }
678     }
679 
680     private void parseConfigurationOptions()
681     {
682         if ( "R7".equalsIgnoreCase( wtpversion ) ) //$NON-NLS-1$
683         {
684             wtpVersionFloat = 0.7f;
685         }
686         else if ( "1.0".equalsIgnoreCase( wtpversion ) ) //$NON-NLS-1$
687         {
688             wtpVersionFloat = 1.0f;
689         }
690         else if ( "1.5".equalsIgnoreCase( wtpversion ) ) //$NON-NLS-1$
691         {
692             wtpVersionFloat = 1.5f;
693         }
694         else if ( "2.0".equalsIgnoreCase( wtpversion ) ) //$NON-NLS-1$
695         {
696             wtpVersionFloat = 2.0f;
697         }
698         if ( !"none".equalsIgnoreCase( wtpversion ) )
699         {
700             getLog().info( Messages.getString( "EclipsePlugin.wtpversion", wtpversion ) );
701         }
702     }
703 
704     protected void setupExtras()
705         throws MojoExecutionException
706     {
707         // extension point.
708     }
709 
710     protected void verifyClasspathContainerListIsComplete()
711     {
712         boolean containsJREContainer = false;
713         // Check if classpathContainer contains a JRE (default, alternate or Execution Environment)
714         for ( Iterator iter = classpathContainers.iterator(); iter.hasNext(); )
715         {
716             Object classPathContainer = iter.next();
717             if ( classPathContainer != null &&
718                 classPathContainer.toString().startsWith( COMMON_PATH_JDT_LAUNCHING_JRE_CONTAINER ) )
719             {
720                 containsJREContainer = true;
721                 break;
722             }
723         }
724         if ( !containsJREContainer )
725         {
726             getLog().warn( Messages.getString( "EclipsePlugin.missingjrecontainer" ) ); //$NON-NLS-1$
727             classpathContainers.add( 0, COMMON_PATH_JDT_LAUNCHING_JRE_CONTAINER );
728         }
729     }
730 
731     private boolean validate()
732         throws MojoExecutionException
733     {
734         // validate sanity of the current m2 project
735         if ( Arrays.binarySearch( WTP_SUPPORTED_VERSIONS, wtpversion ) < 0 )
736         {
737             throw new MojoExecutionException(
738                                               Messages.getString( "EclipsePlugin.unsupportedwtp", new Object[] { //$NON-NLS-1$
739                                                                   wtpversion,
740                                                                       StringUtils.join( WTP_SUPPORTED_VERSIONS, " " ) } ) ); //$NON-NLS-1$
741         }
742 
743         assertNotEmpty( executedProject.getGroupId(), POM_ELT_GROUP_ID ); //$NON-NLS-1$
744         assertNotEmpty( executedProject.getArtifactId(), POM_ELT_ARTIFACT_ID ); //$NON-NLS-1$
745 
746         if ( executedProject.getFile() == null || !executedProject.getFile().exists() )
747         {
748             throw new MojoExecutionException( Messages.getString( "EclipsePlugin.missingpom" ) ); //$NON-NLS-1$
749         }
750 
751         if ( "pom".equals( packaging ) && eclipseProjectDir == null ) //$NON-NLS-1$
752         {
753             getLog().info( Messages.getString( "EclipsePlugin.pompackaging" ) ); //$NON-NLS-1$
754             return false;
755         }
756 
757         if ( "eclipse-plugin".equals( packaging ) )
758         {
759             pde = true;
760         }
761 
762         if ( eclipseProjectDir == null )
763         {
764             eclipseProjectDir = executedProject.getFile().getParentFile();
765         }
766 
767         if ( !eclipseProjectDir.exists() && !eclipseProjectDir.mkdirs() )
768         {
769             throw new MojoExecutionException( Messages.getString( "EclipsePlugin.cantcreatedir", eclipseProjectDir ) ); //$NON-NLS-1$
770         }
771 
772         if ( !eclipseProjectDir.equals( executedProject.getFile().getParentFile() ) )
773         {
774             if ( !eclipseProjectDir.isDirectory() )
775             {
776                 throw new MojoExecutionException( Messages.getString( "EclipsePlugin.notadir", eclipseProjectDir ) ); //$NON-NLS-1$
777             }
778             eclipseProjectDir = new File( eclipseProjectDir, executedProject.getArtifactId() );
779             if ( !eclipseProjectDir.isDirectory() && !eclipseProjectDir.mkdirs() )
780             {
781                 throw new MojoExecutionException( Messages.getString( "EclipsePlugin.cantcreatedir", eclipseProjectDir ) ); //$NON-NLS-1$
782             }
783         }
784 
785         validateExtras();
786 
787         return true;
788     }
789 
790     protected void validateExtras()
791     {
792         // provided for extension.
793     }
794 
795     private void checkDeprecations()
796     {
797         if ( eclipseDownloadSources )
798         {
799             // deprecated warning
800             getLog().warn( Messages.getString( "EclipsePlugin.deprecatedpar", new Object[] { //$NON-NLS-1$
801                                                "eclipse.downloadSources", //$NON-NLS-1$
802                                                    "downloadSources" } ) ); //$NON-NLS-1$
803             downloadSources = true;
804         }
805 
806         checkExtraDeprecations();
807     }
808 
809     protected void checkExtraDeprecations()
810     {
811         // provided for extension.
812     }
813 
814     public void writeConfiguration( IdeDependency[] deps )
815         throws MojoExecutionException
816     {
817         EclipseWriterConfig config = createEclipseWriterConfig( deps );
818 
819         if ( wtpmanifest && isJavaProject() )
820         {
821             EclipseManifestWriter.addManifestResource( getLog(), config );
822         }
823         // NOTE: This could change the config!
824         writeExtraConfiguration( config );
825 
826         if ( wtpVersionFloat == 0.7f )
827         {
828             new EclipseWtpmodulesWriter().init( getLog(), config ).write();
829         }
830 
831         if ( wtpVersionFloat >= 1.0f )
832         {
833             new EclipseWtpFacetsWriter().init( getLog(), config ).write();
834         }
835         if ( wtpVersionFloat == 1.0f )
836         {
837             new EclipseWtpComponentWriter().init( getLog(), config ).write();
838         }
839         if ( wtpVersionFloat >= 1.5 )
840         {
841             new EclipseWtpComponent15Writer().init( getLog(), config ).write();
842         }
843 
844         new EclipseSettingsWriter().init( getLog(), config ).write();
845 
846         if ( isJavaProject )
847         {
848             new EclipseClasspathWriter().init( getLog(), config ).write();
849         }
850 
851         if ( wtpapplicationxml )
852         {
853             new EclipseWtpApplicationXMLWriter().init( getLog(), config ).write();
854         }
855 
856         if ( pde )
857         {
858             this.getLog().info( "The Maven Eclipse plugin runs in 'pde'-mode." );
859             new EclipseOSGiManifestWriter().init( getLog(), config ).write();
860         }
861 
862         // NOTE: This one MUST be after EclipseClasspathwriter, and possibly others,
863         // since currently EclipseClasspathWriter does some magic to detect nested
864         // output folders and modifies the configuration by adding new (Ant) builders.
865         // So the .project file must be written AFTER those have run!
866         new EclipseProjectWriter().init( getLog(), config ).write();
867 
868         if ( additionalConfig != null )
869         {
870             for ( int j = 0; j < additionalConfig.length; j++ )
871             {
872                 EclipseConfigFile file = additionalConfig[j];
873                 File projectRelativeFile = new File( this.eclipseProjectDir, file.getName() );
874                 if ( projectRelativeFile.isDirectory() )
875                 {
876                     // just ignore?
877                     getLog().warn( Messages.getString( "EclipsePlugin.foundadir", //$NON-NLS-1$
878                                                        projectRelativeFile.getAbsolutePath() ) );
879                 }
880 
881                 try
882                 {
883                     projectRelativeFile.getParentFile().mkdirs();
884                     FileUtils.fileWrite( projectRelativeFile.getAbsolutePath(), file.getContent() );
885                 }
886                 catch ( IOException e )
887                 {
888                     throw new MojoExecutionException( Messages.getString( "EclipsePlugin.cantwritetofile", //$NON-NLS-1$
889                                                                           projectRelativeFile.getAbsolutePath() ) );
890                 }
891             }
892         }
893 
894         getLog().info( Messages.getString( "EclipsePlugin.wrote", new Object[] { //$NON-NLS-1$
895                                            config.getEclipseProjectName(), eclipseProjectDir.getAbsolutePath() } ) );
896     }
897 
898     protected EclipseWriterConfig createEclipseWriterConfig( IdeDependency[] deps )
899         throws MojoExecutionException
900     {
901         File projectBaseDir = executedProject.getFile().getParentFile();
902 
903         // build a list of UNIQUE source dirs (both src and resources) to be
904         // used in classpath and wtpmodules
905         EclipseSourceDir[] sourceDirs = buildDirectoryList( executedProject, eclipseProjectDir, buildOutputDirectory );
906 
907         EclipseWriterConfig config = new EclipseWriterConfig();
908 
909         config.setProjectNameTemplate( calculateProjectNameTemplate() );
910 
911         String projectName = IdeUtils.getProjectName( config.getProjectNameTemplate(), project );
912 
913         config.setEclipseProjectName( projectName );
914 
915         config.setWtpapplicationxml( wtpapplicationxml );
916 
917         config.setWtpVersion( this.wtpVersionFloat );
918 
919         Set convertedBuildCommands = new LinkedHashSet();
920 
921         if ( buildcommands != null )
922         {
923             for ( Iterator it = buildcommands.iterator(); it.hasNext(); )
924             {
925                 Object cmd = it.next();
926 
927                 if ( cmd instanceof BuildCommand )
928                 {
929                     convertedBuildCommands.add( (BuildCommand) cmd );
930                 }
931                 else
932                 {
933                     convertedBuildCommands.add( new BuildCommand( (String) cmd ) );
934                 }
935             }
936         }
937 
938         config.setBuildCommands( new LinkedList( convertedBuildCommands ) );
939 
940         config.setBuildOutputDirectory( buildOutputDirectory );
941         config.setClasspathContainers( classpathContainers );
942         config.setDeps( deps );
943         config.setEclipseProjectDirectory( eclipseProjectDir );
944         config.setLocalRepository( localRepository );
945         config.setManifestFile( manifest );
946         config.setPde( pde );
947         config.setProject( project );
948         config.setProjectBaseDir( projectBaseDir );
949         config.setProjectnatures( projectnatures );
950         config.setProjectFacets( additionalProjectFacets );
951         config.setSourceDirs( sourceDirs );
952         config.setAddVersionToProjectName( isAddVersionToProjectName() );
953         config.setPackaging( this.packaging );
954 
955         collectWarContextRootsFromReactorEarConfiguration( config );
956 
957         return config;
958     }
959 
960     /**
961      * If this is a war module peek into the reactor an search for an ear module that defines the context root of this
962      * module.
963      * 
964      * @param config config to save the context root.
965      */
966     private void collectWarContextRootsFromReactorEarConfiguration( EclipseWriterConfig config )
967     {
968         if ( reactorProjects != null && this.wtpContextName == null &&
969             Constants.PROJECT_PACKAGING_WAR.equals( this.project.getPackaging() ) )
970         {
971             for ( Iterator iter = reactorProjects.iterator(); iter.hasNext(); )
972             {
973                 MavenProject reactorProject = (MavenProject) iter.next();
974 
975                 if ( Constants.PROJECT_PACKAGING_EAR.equals( reactorProject.getPackaging() ) )
976                 {
977                     Xpp3Dom[] warDefinitions =
978                         IdeUtils.getPluginConfigurationDom( reactorProject, JeeUtils.ARTIFACT_MAVEN_EAR_PLUGIN,
979                                                             new String[] { "modules", "webModule" } );
980                     for ( int index = 0; index < warDefinitions.length; index++ )
981                     {
982                         Xpp3Dom groupId = warDefinitions[index].getChild( "groupId" );
983                         Xpp3Dom artifactId = warDefinitions[index].getChild( "artifactId" );
984                         Xpp3Dom contextRoot = warDefinitions[index].getChild( "contextRoot" );
985                         if ( groupId != null && artifactId != null && contextRoot != null &&
986                             groupId.getValue() != null && artifactId.getValue() != null &&
987                             contextRoot.getValue() != null )
988                         {
989                             getLog().info(
990                                            "Found context root definition for " + groupId.getValue() + ":" +
991                                                artifactId.getValue() + " " + contextRoot.getValue() );
992                             if ( this.project.getArtifactId().equals( artifactId.getValue() ) &&
993                                 this.project.getGroupId().equals( groupId.getValue() ) )
994                             {
995                                 config.setContextName( contextRoot.getValue() );
996                             }
997                         }
998                         else
999                         {
1000                             getLog().info(
1001                                            "Found incomplete ear configuration in " + reactorProject.getGroupId() +
1002                                                ":" + reactorProject.getGroupId() + " found " +
1003                                                warDefinitions[index].toString() );
1004                         }
1005                     }
1006                 }
1007             }
1008         }
1009         if ( config.getContextName() == null && Constants.PROJECT_PACKAGING_WAR.equals( this.project.getPackaging() ) )
1010         {
1011             if ( this.wtpContextName == null )
1012             {
1013                 config.setContextName( this.project.getArtifactId() );
1014             }
1015             else
1016             {
1017                 config.setContextName( this.wtpContextName );
1018             }
1019         }
1020     }
1021 
1022     /**
1023      * Write any extra configuration information for the Eclipse project. This is an extension point, called before the
1024      * main configurations are written. <br/> <b> NOTE: This could change the config! </b>
1025      * 
1026      * @param config
1027      * @throws MojoExecutionException
1028      */
1029     protected void writeExtraConfiguration( EclipseWriterConfig config )
1030         throws MojoExecutionException
1031     {
1032         // extension point.
1033     }
1034 
1035     private void assertNotEmpty( String string, String elementName )
1036         throws MojoExecutionException
1037     {
1038         if ( string == null )
1039         {
1040             throw new MojoExecutionException( Messages.getString( "EclipsePlugin.missingelement", elementName ) ); //$NON-NLS-1$
1041         }
1042     }
1043 
1044     protected void fillDefaultNatures( String packaging )
1045     {
1046         projectnatures = new ArrayList();
1047 
1048         if ( wtpVersionFloat >= 1.0f )
1049         {
1050             projectnatures.add( NATURE_WST_FACET_CORE_NATURE ); // WTP 1.0 nature
1051         }
1052 
1053         if ( isJavaProject )
1054         {
1055             projectnatures.add( NATURE_JDT_CORE_JAVA );
1056         }
1057 
1058         if ( wtpVersionFloat >= 0.7f )
1059         {
1060             projectnatures.add( NATURE_WST_MODULE_CORE_NATURE ); // WTP 0.7/1.0 nature
1061 
1062             if ( isJavaProject )
1063             {
1064                 projectnatures.add( NATURE_JEM_WORKBENCH_JAVA_EMF ); // WTP 0.7/1.0 nature
1065             }
1066         }
1067 
1068         if ( pde )
1069         {
1070             projectnatures.add( NATURE_PDE_PLUGIN );
1071         }
1072 
1073     }
1074 
1075     protected void fillDefaultClasspathContainers( String packaging )
1076     {
1077         classpathContainers = new ArrayList();
1078         classpathContainers.add( COMMON_PATH_JDT_LAUNCHING_JRE_CONTAINER );
1079 
1080         if ( pde )
1081         {
1082             classpathContainers.add( REQUIRED_PLUGINS_CONTAINER );
1083         }
1084     }
1085 
1086     protected void fillDefaultBuilders( String packaging )
1087     {
1088         buildcommands = new ArrayList();
1089 
1090         if ( wtpVersionFloat == 0.7f )
1091         {
1092             buildcommands.add( new BuildCommand( BUILDER_WST_COMPONENT_STRUCTURAL ) ); // WTP 0.7 builder
1093         }
1094 
1095         if ( isJavaProject )
1096         {
1097             buildcommands.add( new BuildCommand( BUILDER_JDT_CORE_JAVA ) );
1098         }
1099 
1100         if ( wtpVersionFloat >= 1.5f )
1101         {
1102             buildcommands.add( new BuildCommand( BUILDER_WST_FACET ) ); // WTP 1.5 builder
1103         }
1104 
1105         if ( wtpVersionFloat >= 0.7f )
1106         {
1107             buildcommands.add( new BuildCommand( BUILDER_WST_VALIDATION ) ); // WTP 0.7/1.0 builder
1108         }
1109 
1110         if ( wtpVersionFloat == 0.7f )
1111         {
1112             // WTP 0.7 builder
1113             buildcommands.add( new BuildCommand( BUILDER_WST_COMPONENT_STRUCTURAL_DEPENDENCY_RESOLVER ) );
1114         }
1115 
1116         if ( pde )
1117         {
1118             buildcommands.add( new BuildCommand( BUILDER_PDE_MANIFEST ) );
1119             buildcommands.add( new BuildCommand( BUILDER_PDE_SCHEMA ) );
1120         }
1121     }
1122 
1123     public EclipseSourceDir[] buildDirectoryList( MavenProject project, File basedir, File buildOutputDirectory )
1124         throws MojoExecutionException
1125     {
1126         File projectBaseDir = project.getFile().getParentFile();
1127 
1128         // avoid duplicated entries
1129         Set directories = new TreeSet();
1130 
1131         extractSourceDirs( directories, project.getCompileSourceRoots(), basedir, projectBaseDir, false, null );
1132 
1133         String relativeOutput = IdeUtils.toRelativeAndFixSeparator( projectBaseDir, buildOutputDirectory, false );
1134 
1135         extractResourceDirs( directories, project.getBuild().getResources(), project, basedir, projectBaseDir, false,
1136                              relativeOutput );
1137 
1138         // If using the standard output location, don't mix the test output into it.
1139         String testOutput = null;
1140         boolean useStandardOutputDir =
1141             buildOutputDirectory.equals( new File( project.getBuild().getOutputDirectory() ) );
1142         if ( useStandardOutputDir )
1143         {
1144             getLog().debug(
1145                             "testOutput toRelativeAndFixSeparator " + projectBaseDir + " , " +
1146                                 project.getBuild().getTestOutputDirectory() );
1147             testOutput =
1148                 IdeUtils.toRelativeAndFixSeparator( projectBaseDir,
1149                                                     new File( project.getBuild().getTestOutputDirectory() ), false );
1150             getLog().debug( "testOutput after toRelative : " + testOutput );
1151         }
1152 
1153         extractSourceDirs( directories, project.getTestCompileSourceRoots(), basedir, projectBaseDir, true, testOutput );
1154 
1155         extractResourceDirs( directories, project.getBuild().getTestResources(), project, basedir, projectBaseDir,
1156                              true, testOutput );
1157 
1158         return (EclipseSourceDir[]) directories.toArray( new EclipseSourceDir[directories.size()] );
1159     }
1160 
1161     private void extractSourceDirs( Set directories, List sourceRoots, File basedir, File projectBaseDir, boolean test,
1162                                     String output )
1163         throws MojoExecutionException
1164     {
1165         for ( Iterator it = sourceRoots.iterator(); it.hasNext(); )
1166         {
1167 
1168             File sourceRootFile = new File( (String) it.next() );
1169 
1170             if ( sourceRootFile.isDirectory() )
1171             {
1172                 String sourceRoot =
1173                     IdeUtils.toRelativeAndFixSeparator( projectBaseDir, sourceRootFile,
1174                                                         !projectBaseDir.equals( basedir ) );
1175 
1176                 directories.add( new EclipseSourceDir( sourceRoot, output, false, test, null, null, false ) );
1177             }
1178         }
1179     }
1180 
1181     void extractResourceDirs( Set directories, List resources, MavenProject project, File basedir,
1182                               File workspaceProjectBaseDir, boolean test, final String output )
1183         throws MojoExecutionException
1184     {
1185         for ( Iterator it = resources.iterator(); it.hasNext(); )
1186         {
1187             Resource resource = (Resource) it.next();
1188 
1189             getLog().debug( "Processing resource dir: " + resource.getDirectory() );
1190 
1191             String includePattern = null;
1192             String excludePattern = null;
1193 
1194             if ( resource.getIncludes().size() != 0 )
1195             {
1196                 includePattern = StringUtils.join( resource.getIncludes().iterator(), "|" );
1197             }
1198 
1199             if ( resource.getExcludes().size() != 0 )
1200             {
1201                 excludePattern = StringUtils.join( resource.getExcludes().iterator(), "|" );
1202             }
1203 
1204             // TODO: figure out how to merge if the same dir is specified twice
1205             // with different in/exclude patterns.
1206 
1207             File resourceDirectory = new File( /* basedir, */resource.getDirectory() );
1208 
1209             if ( !resourceDirectory.exists() || !resourceDirectory.isDirectory() )
1210             {
1211                 getLog().debug( "Resource dir: " + resourceDirectory + " either missing or not a directory." );
1212                 continue;
1213             }
1214 
1215             String resourceDir =
1216                 IdeUtils.toRelativeAndFixSeparator( workspaceProjectBaseDir, resourceDirectory,
1217                                                     !workspaceProjectBaseDir.equals( basedir ) );
1218             String thisOutput = output;
1219             if ( thisOutput != null )
1220             {
1221                 // sometimes thisOutput is already an absolute path
1222                 File outputFile = new File( thisOutput );
1223                 if ( !outputFile.isAbsolute() )
1224                 {
1225                     outputFile = new File( workspaceProjectBaseDir, thisOutput );
1226                 }
1227                 // create output dir if it doesn't exist
1228                 outputFile.mkdirs();
1229 
1230                 if ( !StringUtils.isEmpty( resource.getTargetPath() ) )
1231                 {
1232                     outputFile = new File( outputFile, resource.getTargetPath() );
1233                     // create output dir if it doesn't exist
1234                     outputFile.mkdirs();
1235                 }
1236 
1237                 getLog().debug(
1238                                 "Making relative and fixing separator: { " + workspaceProjectBaseDir + ", " +
1239                                     outputFile + ", false }." );
1240                 thisOutput = IdeUtils.toRelativeAndFixSeparator( workspaceProjectBaseDir, outputFile, false );
1241             }
1242 
1243             getLog().debug(
1244                             "Adding eclipse source dir: { " + resourceDir + ", " + thisOutput + ", true, " + test +
1245                                 ", " + includePattern + ", " + excludePattern + " }." );
1246 
1247             directories.add( new EclipseSourceDir( resourceDir, thisOutput, true, test, includePattern, excludePattern,
1248                                                    resource.isFiltering() ) );
1249         }
1250     }
1251 
1252     /**
1253      * Calculate the project name template from the fields {@link #projectNameTemplate},
1254      * {@link #addVersionToProjectName} and {@link #addGroupIdToProjectName}
1255      * 
1256      * @return the project name template that should be used after considering the plugin configuration
1257      */
1258     private String calculateProjectNameTemplate()
1259     {
1260         if ( getProjectNameTemplate() != null )
1261         {
1262             if ( isAddVersionToProjectName() || isAddGroupIdToProjectName() )
1263             {
1264                 getLog().warn(
1265                                "projectNameTemplate definition overrides "
1266                                    + "addVersionToProjectName or addGroupIdToProjectName" );
1267             }
1268             return getProjectNameTemplate();
1269         }
1270         else if ( isAddVersionToProjectName() && isAddGroupIdToProjectName() )
1271         {
1272             return IdeUtils.PROJECT_NAME_WITH_GROUP_AND_VERSION_TEMPLATE;
1273         }
1274         else if ( isAddVersionToProjectName() )
1275         {
1276             return IdeUtils.PROJECT_NAME_WITH_VERSION_TEMPLATE;
1277         }
1278         else if ( isAddGroupIdToProjectName() )
1279         {
1280             return IdeUtils.PROJECT_NAME_WITH_GROUP_TEMPLATE;
1281         }
1282         return IdeUtils.PROJECT_NAME_DEFAULT_TEMPLATE;
1283     }
1284 
1285     /**
1286      * {@inheritDoc}
1287      */
1288     public String getProjectNameForArifact( Artifact artifact )
1289     {
1290         return IdeUtils.getProjectName( calculateProjectNameTemplate(), artifact );
1291     }
1292 }