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