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 }