View Javadoc
1   package org.apache.maven.plugins.ear;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.Set;
23  
24  import org.apache.maven.artifact.Artifact;
25  import org.apache.maven.plugin.MojoFailureException;
26  import org.apache.maven.plugins.ear.util.ArtifactRepository;
27  import org.apache.maven.shared.mapping.MappingUtils;
28  import org.codehaus.plexus.interpolation.InterpolationException;
29  import org.codehaus.plexus.util.xml.XMLWriter;
30  
31  /**
32   * A base implementation of an {@link EarModule}.
33   * 
34   * @author <a href="snicoll@apache.org">Stephane Nicoll</a>
35   */
36  public abstract class AbstractEarModule
37      implements EarModule
38  {
39  
40      /**
41       * The module element.
42       */
43      protected static final String MODULE_ELEMENT = "module";
44  
45      /**
46       * The java module.
47       */
48      protected static final String JAVA_MODULE = "java";
49  
50      /**
51       * The alt-dd module.
52       */
53      protected static final String ALT_DD = "alt-dd";
54  
55      private Artifact artifact;
56  
57      // Those are set by the configuration
58  
59      private String groupId;
60  
61      private String artifactId;
62  
63      /**
64       * The type of the artifact
65       */
66      protected String type;
67  
68      private String classifier;
69  
70      /**
71       * The bundleDir.
72       */
73      protected String bundleDir;
74  
75      /**
76       * The bundleFileName.
77       */
78      protected String bundleFileName;
79  
80      /**
81       * excluded by default {@code false}.
82       */
83      protected Boolean excluded = Boolean.FALSE;
84  
85      private String uri;
86  
87      /**
88       * unpack
89       */
90      protected Boolean unpack = null;
91  
92      /**
93       * The alternate deployment descriptor.
94       */
95      protected String altDeploymentDescriptor;
96  
97      private String moduleId;
98  
99      /**
100      * Directory of module which contains libraries packaged into module. {@code null} value means that module
101      * doesn't contain any library. Each module type can provide default value for this directory and this option
102      * can be used to override that default value. If module libraries are located at the root of module then use
103      * single slash (/) to configure that in POM. That is, a single slash is treated as an empty string.
104      */
105     protected String libDirectory;
106 
107     /**
108      * If module is considered for inclusion into the Class-Path entry of MANIFEST.mf of other modules. {@code false}
109      * value leads to removal of the module from the Class-Path entry. {@code true} value leads to modification of the
110      * reference to the module in the Class-Path entry if such reference exists or leads to adding of the module into
111      * the Class-Path entry if such reference doesn't exist. Removal, modification or adding of the reference in the
112      * Class-Path entry depends on libDirectory property of another module and on skinnyWars / skinnyModules parameters
113      * of EAR Plugin.
114      */
115     protected boolean classPathItem;
116 
117     // This is injected once the module has been built.
118 
119     /**
120      * The {@link EarExecutionContext}
121      */
122     protected EarExecutionContext earExecutionContext;
123 
124     /**
125      * Empty constructor to be used when the module is built based on the configuration.
126      */
127     public AbstractEarModule()
128     {
129     }
130 
131     /**
132      * Creates an ear module from the artifact.
133      * 
134      * @param a the artifact
135      */
136     public AbstractEarModule( Artifact a )
137     {
138         this.artifact = a;
139         this.groupId = a.getGroupId();
140         this.artifactId = a.getArtifactId();
141         this.type = a.getType();
142         this.classifier = a.getClassifier();
143         this.bundleDir = null;
144     }
145 
146     /**
147      * {@inheritDoc}
148      */
149     public void setEarExecutionContext( EarExecutionContext earExecutionContext )
150     {
151         this.earExecutionContext = earExecutionContext;
152     }
153 
154     /** {@inheritDoc} */
155     public void resolveArtifact( Set<Artifact> artifacts )
156         throws EarPluginException, MojoFailureException
157     {
158         // If the artifact is already set no need to resolve it
159         if ( artifact == null )
160         {
161             // Make sure that at least the groupId and the artifactId are specified
162             if ( groupId == null || artifactId == null )
163             {
164                 throw new MojoFailureException( "Could not resolve artifact[" + groupId + ":" + artifactId + ":"
165                     + getType() + "]" );
166             }
167             final ArtifactRepository ar = earExecutionContext.getArtifactRepository();
168             artifact = ar.getUniqueArtifact( groupId, artifactId, getType(), classifier );
169             // Artifact has not been found
170             if ( artifact == null )
171             {
172                 Set<Artifact> candidates = ar.getArtifacts( groupId, artifactId, getType() );
173                 if ( candidates.size() > 1 )
174                 {
175                     throw new MojoFailureException( "Artifact[" + this + "] has " + candidates.size()
176                         + " candidates, please provide a classifier." );
177                 }
178                 else
179                 {
180                     throw new MojoFailureException( "Artifact[" + this + "] is not a dependency of the project." );
181                 }
182             }
183         }
184     }
185 
186     /**
187      * @return {@link #artifact}
188      */
189     public Artifact getArtifact()
190     {
191         return artifact;
192     }
193 
194     /**
195      * @return {@link #moduleId}
196      */
197     public String getModuleId()
198     {
199         return moduleId;
200     }
201 
202     /**
203      * @return Return the URI.
204      */
205     public String getUri()
206     {
207         if ( uri == null )
208         {
209             if ( getBundleDir() == null )
210             {
211                 uri = getBundleFileName();
212             }
213             else
214             {
215                 uri = getBundleDir() + getBundleFileName();
216             }
217         }
218         return uri;
219     }
220 
221     /**
222      * {@inheritDoc}
223      */
224     public String getType()
225     {
226         return type;
227     }
228 
229     /**
230      * Returns the artifact's groupId.
231      * 
232      * @return {@link #groupId}
233      */
234     public String getGroupId()
235     {
236         return groupId;
237     }
238 
239     /**
240      * Returns the artifact's Id.
241      * 
242      * @return {@link #artifactId}
243      */
244     public String getArtifactId()
245     {
246         return artifactId;
247     }
248 
249     /**
250      * Returns the artifact's classifier.
251      * 
252      * @return the artifact classifier
253      */
254     public String getClassifier()
255     {
256         return classifier;
257     }
258 
259     /**
260      * Returns the bundle directory. If null, the module is bundled in the root of the EAR.
261      * 
262      * @return the custom bundle directory
263      */
264     public String getBundleDir()
265     {
266         bundleDir = cleanArchivePath( bundleDir );
267         return bundleDir;
268     }
269 
270     /**
271      * {@inheritDoc}
272      */
273     public String getLibDir()
274     {
275         libDirectory = cleanArchivePath( libDirectory );
276         return libDirectory;
277     }
278 
279     /**
280      * {@inheritDoc}
281      */
282     public boolean isClassPathItem()
283     {
284         return classPathItem;
285     }
286 
287     /**
288      * {@inheritDoc}
289      */
290     public String getBundleFileName()
291     {
292         if ( bundleFileName == null )
293         {
294             try
295             {
296                 String outputFileNameMapping = earExecutionContext.getOutputFileNameMapping();
297                 bundleFileName = MappingUtils.evaluateFileNameMapping( outputFileNameMapping, artifact );
298             }
299             catch ( InterpolationException e )
300             {
301                 // We currently ignore this here, cause assumption is that
302                 // has already been happened before..
303                 // FIXME: Should be checked first.
304             }
305 
306             // bundleFileName = earExecutionContext.getFileNameMapping().mapFileName( artifact );
307         }
308         return bundleFileName;
309     }
310 
311     /**
312      * The alt-dd element specifies an optional URI to the post-assembly version of the deployment descriptor file for a
313      * particular Java EE module. The URI must specify the full pathname of the deployment descriptor file relative to
314      * the application's root directory.
315      * 
316      * @return the alternative deployment descriptor for this module
317      */
318     public String getAltDeploymentDescriptor()
319     {
320         return altDeploymentDescriptor;
321     }
322 
323     /**
324      * Specify whether this module should be excluded or not.
325      * 
326      * @return true if this module should be skipped, false otherwise
327      */
328     public boolean isExcluded()
329     {
330         return excluded;
331     }
332 
333     /**
334      * @return {@link #unpack}
335      */
336     public Boolean shouldUnpack()
337     {
338         return unpack;
339     }
340 
341     /**
342      * Writes the alternative deployment descriptor if necessary.
343      * 
344      * @param writer the writer to use
345      * @param version the java EE version in use
346      */
347     protected void writeAltDeploymentDescriptor( XMLWriter writer, String version )
348     {
349         if ( getAltDeploymentDescriptor() != null )
350         {
351             writer.startElement( ALT_DD );
352             writer.writeText( getAltDeploymentDescriptor() );
353             writer.endElement();
354         }
355     }
356 
357     /**
358      * Starts a new {@link #MODULE_ELEMENT} on the specified writer, possibly including an id attribute.
359      * 
360      * @param writer the XML writer.
361      * @param generateId whether an id should be generated
362      */
363     protected void startModuleElement( XMLWriter writer, Boolean generateId )
364     {
365         writer.startElement( MODULE_ELEMENT );
366 
367         // If a moduleId is specified, always include it
368         if ( getModuleId() != null )
369         {
370             writer.addAttribute( "id", getModuleId() );
371         }
372         else if ( generateId )
373         {
374             // No module id was specified but one should be generated.
375             // FIXME: Should we use the mapping using outputFileNameMapping instead
376             // of doing this on our own?
377             Artifact theArtifact = getArtifact();
378             String generatedId = theArtifact.getType().toUpperCase() + "_" + theArtifact.getGroupId() + "."
379                 + theArtifact.getArtifactId();
380             if ( null != theArtifact.getClassifier() && theArtifact.getClassifier().trim().length() > 0 )
381             {
382                 generatedId += "-" + theArtifact.getClassifier().trim();
383             }
384             writer.addAttribute( "id", generatedId );
385         }
386     }
387 
388     /**
389      * {@inheritDoc}
390      */
391     public String toString()
392     {
393         StringBuilder sb = new StringBuilder();
394         sb.append( getType() ).append( ":" ).append( groupId ).append( ":" ).append( artifactId );
395         if ( classifier != null )
396         {
397             sb.append( ":" ).append( classifier );
398         }
399         if ( artifact != null )
400         {
401             sb.append( ":" ).append( artifact.getVersion() );
402         }
403         return sb.toString();
404     }
405 
406     /**
407      * Cleans the path pointing to the resource inside the archive so that it might be used properly.
408      * 
409      * @param path the path to clean, can be {@code null}
410      * @return the cleaned path or {@code null} if given {@code path} is {@code null}
411      */
412     static String cleanArchivePath( String path )
413     {
414         if ( path == null )
415         {
416             return null;
417         }
418 
419         // Using slashes
420         path = path.replace( '\\', '/' );
421 
422         // Remove '/' prefix if any so that path is a relative path
423         if ( path.startsWith( "/" ) )
424         {
425             path = path.substring( 1, path.length() );
426         }
427 
428         if ( path.length() > 0 && !path.endsWith( "/" ) )
429         {
430             // Adding '/' suffix to specify a path structure if it is not empty
431             path = path + "/";
432         }
433 
434         return path;
435     }
436 
437     /**
438      * Sets the URI of the module explicitly for testing purposes.
439      * 
440      * @param uri the uri
441      */
442     void setUri( String uri )
443     {
444         this.uri = uri;
445 
446     }
447 
448     /**
449      * @return always {@code true}
450      */
451     public boolean changeManifestClasspath()
452     {
453         return true;
454     }
455 }