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 }