1 package org.apache.maven.plugin.ejb;
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 org.apache.maven.archiver.MavenArchiveConfiguration;
23 import org.apache.maven.archiver.MavenArchiver;
24 import org.apache.maven.artifact.DependencyResolutionRequiredException;
25 import org.apache.maven.execution.MavenSession;
26 import org.apache.maven.plugin.AbstractMojo;
27 import org.apache.maven.plugin.MojoExecutionException;
28 import org.apache.maven.project.MavenProject;
29 import org.apache.maven.project.MavenProjectHelper;
30 import org.apache.maven.shared.filtering.MavenFileFilter;
31 import org.apache.maven.shared.filtering.MavenFilteringException;
32 import org.apache.maven.shared.filtering.MavenResourcesExecution;
33 import org.codehaus.plexus.archiver.ArchiverException;
34 import org.codehaus.plexus.archiver.jar.JarArchiver;
35 import org.codehaus.plexus.archiver.jar.ManifestException;
36 import org.codehaus.plexus.util.FileUtils;
37 import org.codehaus.plexus.util.xml.XmlStreamReader;
38
39 import java.io.File;
40 import java.io.IOException;
41 import java.util.List;
42
43 /**
44 * Build an EJB (and optional client) from the current project.
45 *
46 * @author <a href="evenisse@apache.org">Emmanuel Venisse</a>
47 * @version $Id: EjbMojo.java 997273 2010-09-15 10:45:28Z dennisl $
48 * @goal ejb
49 * @requiresDependencyResolution runtime
50 * @threadSafe
51 * @phase package
52 */
53 public class EjbMojo
54 extends AbstractMojo
55 {
56 private static final String EJB_JAR_XML = "META-INF/ejb-jar.xml";
57
58 // TODO: will null work instead?
59 private static final String[] DEFAULT_INCLUDES = new String[]{"**/**"};
60
61 private static final String[] DEFAULT_EXCLUDES = new String[]{EJB_JAR_XML, "**/package.html"};
62
63 private static final String[] DEFAULT_CLIENT_EXCLUDES =
64 new String[]{"**/*Bean.class", "**/*CMP.class", "**/*Session.class", "**/package.html"};
65
66 private static final String[] EMPTY_STRING_ARRAY = new String[0];
67
68 /**
69 * The directory for the generated EJB.
70 *
71 * @parameter default-value="${project.build.directory}"
72 * @required
73 * @readonly
74 */
75 private File basedir;
76
77 /**
78 * Directory that resources are copied to during the build.
79 *
80 * @parameter default-value="${project.build.outputDirectory}" expression="${outputDirectory}"
81 */
82 private File outputDirectory;
83
84 /**
85 * The name of the EJB file to generate.
86 *
87 * @parameter default-value="${project.build.finalName}" expression="${jarName}"
88 */
89 private String jarName;
90
91 /**
92 * Classifier to add to the artifact generated. If given, the artifact will
93 * be an attachment instead.
94 *
95 * @parameter expression="${ejb.classifier}"
96 */
97 private String classifier;
98
99 /**
100 * Whether the EJB client jar should be generated or not.
101 *
102 * @parameter default-value="false" expression="${ejb.generateClient}"
103 */
104 private boolean generateClient;
105
106 /**
107 * The files and directories to exclude from the client jar. Usage:
108 *
109 * <pre>
110 * <clientExcludes>
111 * <clientExclude>**/*Ejb.class</clientExclude>
112 * <clientExclude>**/*Bean.class</clientExclude>
113 * </clientExcludes>
114 * </pre>
115 * <br/>Attribute is used only if client jar is generated.
116 * <br/>Default exclusions: **/*Bean.class, **/*CMP.class, **/*Session.class, **/package.html
117 *
118 * @parameter
119 */
120 private List clientExcludes;
121
122 /**
123 * The files and directories to include in the client jar. Usage:
124 *
125 * <pre>
126 * <clientIncludes>
127 * <clientInclude>**/*</clientInclude>
128 * </clientIncludes>
129 * </pre>
130 * <br/>Attribute is used only if client jar is generated.
131 * <br/>Default value: **/**
132 *
133 * @parameter
134 */
135 private List clientIncludes;
136
137 /**
138 * The files and directories to exclude from the main EJB jar. Usage:
139 *
140 * <pre>
141 * <excludes>
142 * <exclude>**/*Ejb.class</exclude>
143 * <exclude>**/*Bean.class</exclude>
144 * </excludes>
145 * </pre>
146 * <br/>Default exclusions: META-INF/ejb-jar.xml, **/package.html
147 * @parameter
148 */
149 private List excludes;
150
151 /**
152 * The Maven project.
153 *
154 * @parameter default-value="${project}"
155 * @required
156 * @readonly
157 */
158 private MavenProject project;
159
160 /**
161 * The Jar archiver.
162 *
163 * @component role="org.codehaus.plexus.archiver.Archiver" roleHint="jar"
164 */
165 private JarArchiver jarArchiver;
166
167 /**
168 * What EJB version should the EJB Plugin generate? Valid values are "2.x" or "3.x"
169 * (where x is a digit). When ejbVersion is "3.x", the
170 * <code>ejb-jar.xml</code> file is optional.
171 * <p/>
172 * Usage:
173 * <pre>
174 * <ejbVersion>3.0</ejbVersion>
175 * </pre>
176 *
177 * @parameter default-value="2.1" expression="${ejb.ejbVersion}"
178 * @since 2.1
179 */
180 private String ejbVersion;
181
182 /**
183 * The client Jar archiver.
184 *
185 * @component role="org.codehaus.plexus.archiver.Archiver" roleHint="jar"
186 */
187 private JarArchiver clientJarArchiver;
188
189 /**
190 * The Maven project's helper.
191 *
192 * @component
193 */
194 private MavenProjectHelper projectHelper;
195
196 /**
197 * The archive configuration to use.
198 * See <a href="http://maven.apache.org/shared/maven-archiver/index.html">Maven Archiver Reference</a>.
199 * This version of the EJB Plugin uses Maven Archiver 2.4.
200 *
201 * @parameter
202 */
203 private MavenArchiveConfiguration archive = new MavenArchiveConfiguration();
204
205 /**
206 * To escape interpolated value with windows path.
207 * c:\foo\bar will be replaced with c:\\foo\\bar.
208 *
209 * @parameter default-value="false" expression="${ejb.escapeBackslashesInFilePath}"
210 * @since 2.3
211 */
212 private boolean escapeBackslashesInFilePath;
213
214 /**
215 * An expression preceded with this String won't be interpolated.
216 * \${foo} will be replaced with ${foo}.
217 *
218 * @parameter expression="${ejb.escapeString}"
219 * @since 2.3
220 */
221 protected String escapeString;
222
223 /**
224 * To filter the deployment descriptor.
225 *
226 * @parameter default-value="false" expression="${ejb.filterDeploymentDescriptor}"
227 * @since 2.3
228 */
229 private boolean filterDeploymentDescriptor;
230
231 /**
232 * Filters (properties files) to include during the interpolation of the deployment descriptor.
233 *
234 * @parameter
235 * @since 2.3
236 */
237 private List filters;
238
239 /**
240 * @component role="org.apache.maven.shared.filtering.MavenFileFilter" role-hint="default"
241 * @required
242 * @since 2.3
243 */
244 private MavenFileFilter mavenFileFilter;
245
246 /**
247 * @parameter expression="${session}"
248 * @readonly
249 * @required
250 * @since 2.3
251 */
252 private MavenSession session;
253
254 /**
255 * Generates an EJB jar and optionally an ejb-client jar.
256 *
257 * @todo Add license files in META-INF directory.
258 */
259 public void execute()
260 throws MojoExecutionException
261 {
262 if ( getLog().isInfoEnabled() )
263 {
264 getLog().info( "Building EJB " + jarName + " with EJB version " + ejbVersion );
265 }
266
267 File jarFile = getEJBJarFile( basedir, jarName, classifier );
268
269 MavenArchiver archiver = new MavenArchiver();
270
271 archiver.setArchiver( jarArchiver );
272
273 archiver.setOutputFile( jarFile );
274
275 File deploymentDescriptor = new File( outputDirectory, EJB_JAR_XML );
276
277 /* test EJB version compliance */
278 if ( !ejbVersion.matches( "\\A[2-3]\\.[0-9]\\z" ) )
279 {
280 throw new MojoExecutionException(
281 "ejbVersion is not valid: " + ejbVersion + ". Must be 2.x or 3.x (where x is a digit)" );
282 }
283
284 if ( ejbVersion.matches( "\\A2\\.[0-9]\\z" ) && !deploymentDescriptor.exists() )
285 {
286 throw new MojoExecutionException(
287 "Error assembling EJB: " + EJB_JAR_XML + " is required for ejbVersion 2.x" );
288 }
289
290 try
291 {
292 String[] mainJarExcludes = DEFAULT_EXCLUDES;
293
294 if ( excludes != null && !excludes.isEmpty() )
295 {
296 excludes.add( EJB_JAR_XML );
297 mainJarExcludes = (String[]) excludes.toArray( EMPTY_STRING_ARRAY );
298 }
299
300 archiver.getArchiver().addDirectory( outputDirectory, DEFAULT_INCLUDES, mainJarExcludes );
301
302 if ( deploymentDescriptor.exists() )
303 {
304 // EJB-34 Filter ejb-jar.xml
305 if ( filterDeploymentDescriptor )
306 {
307 getLog().debug( "Filtering deployment descriptor." );
308 MavenResourcesExecution mavenResourcesExecution = new MavenResourcesExecution();
309 mavenResourcesExecution.setEscapeString( escapeString );
310 List filterWrappers = mavenFileFilter.getDefaultFilterWrappers( project, filters,
311 escapeBackslashesInFilePath,
312 this.session,
313 mavenResourcesExecution );
314
315 // Create a temporary file that we can copy-and-filter
316 File unfilteredDeploymentDescriptor = new File( outputDirectory, EJB_JAR_XML + ".unfiltered" );
317 FileUtils.copyFile( deploymentDescriptor, unfilteredDeploymentDescriptor );
318 mavenFileFilter.copyFile( unfilteredDeploymentDescriptor, deploymentDescriptor, true,
319 filterWrappers, getEncoding( unfilteredDeploymentDescriptor ) );
320 // Remove the temporary file
321 FileUtils.forceDelete( unfilteredDeploymentDescriptor );
322 }
323 archiver.getArchiver().addFile( deploymentDescriptor, EJB_JAR_XML );
324 }
325
326 // create archive
327 archiver.createArchive( project, archive );
328 }
329 catch ( ArchiverException e )
330 {
331 throw new MojoExecutionException( "There was a problem creating the EJB archive: " + e.getMessage(), e );
332 }
333 catch ( ManifestException e )
334 {
335 throw new MojoExecutionException( "There was a problem creating the EJB archive: " + e.getMessage(), e );
336 }
337 catch ( IOException e )
338 {
339 throw new MojoExecutionException( "There was a problem creating the EJB archive: " + e.getMessage(), e );
340 }
341 catch ( DependencyResolutionRequiredException e )
342 {
343 throw new MojoExecutionException( "There was a problem creating the EJB archive: " + e.getMessage(), e );
344 }
345 catch ( MavenFilteringException e )
346 {
347 throw new MojoExecutionException( "There was a problem filtering the deployment descriptor: " + e.getMessage(), e );
348 }
349
350 // Handle the classifier if necessary
351 if ( classifier != null )
352 {
353 projectHelper.attachArtifact( project, "ejb", classifier, jarFile );
354 }
355 else
356 {
357 project.getArtifact().setFile( jarFile );
358 }
359
360 if ( generateClient )
361 {
362 String clientJarName = jarName;
363 if ( classifier != null )
364 {
365 clientJarName += "-" + classifier;
366 }
367
368 getLog().info( "Building EJB client " + clientJarName + "-client" );
369
370 String[] excludes = DEFAULT_CLIENT_EXCLUDES;
371 String[] includes = DEFAULT_INCLUDES;
372
373 if ( clientIncludes != null && !clientIncludes.isEmpty() )
374 {
375 includes = (String[]) clientIncludes.toArray( EMPTY_STRING_ARRAY );
376 }
377
378 if ( clientExcludes != null && !clientExcludes.isEmpty() )
379 {
380 excludes = (String[]) clientExcludes.toArray( EMPTY_STRING_ARRAY );
381 }
382
383 File clientJarFile = new File( basedir, clientJarName + "-client.jar" );
384
385 MavenArchiver clientArchiver = new MavenArchiver();
386
387 clientArchiver.setArchiver( clientJarArchiver );
388
389 clientArchiver.setOutputFile( clientJarFile );
390
391 try
392 {
393 clientArchiver.getArchiver().addDirectory( outputDirectory, includes, excludes );
394
395 // create archive
396 clientArchiver.createArchive( project, archive );
397
398 }
399 catch ( ArchiverException e )
400 {
401 throw new MojoExecutionException(
402 "There was a problem creating the EJB client archive: " + e.getMessage(), e );
403 }
404 catch ( ManifestException e )
405 {
406 throw new MojoExecutionException(
407 "There was a problem creating the EJB client archive: " + e.getMessage(), e );
408 }
409 catch ( IOException e )
410 {
411 throw new MojoExecutionException(
412 "There was a problem creating the EJB client archive: " + e.getMessage(), e );
413 }
414 catch ( DependencyResolutionRequiredException e )
415 {
416 throw new MojoExecutionException(
417 "There was a problem creating the EJB client archive: " + e.getMessage(), e );
418 }
419
420 // TODO: shouldn't need classifer
421 if ( classifier != null )
422 {
423 projectHelper.attachArtifact( project, "ejb-client", classifier + "-client", clientJarFile );
424 }
425 else
426 {
427 projectHelper.attachArtifact( project, "ejb-client", "client", clientJarFile );
428 }
429 }
430 }
431
432 /**
433 * Returns the EJB Jar file to generate, based on an optional classifier.
434 *
435 * @param basedir the output directory
436 * @param finalName the name of the ear file
437 * @param classifier an optional classifier
438 * @return the EJB file to generate
439 */
440 private static File getEJBJarFile( File basedir, String finalName, String classifier )
441 {
442 if ( classifier == null )
443 {
444 classifier = "";
445 }
446 else if ( classifier.trim().length() > 0 && !classifier.startsWith( "-" ) )
447 {
448 classifier = "-" + classifier;
449 }
450
451 return new File( basedir, finalName + classifier + ".jar" );
452 }
453
454 /**
455 * Get the encoding from an XML-file.
456 *
457 * @param xmlFile the XML-file
458 * @return The encoding of the XML-file, or UTF-8 if it's not specified in the file
459 * @throws IOException if an error occurred while reading the file
460 */
461 private String getEncoding( File xmlFile )
462 throws IOException
463 {
464 XmlStreamReader xmlReader = new XmlStreamReader( xmlFile );
465 return xmlReader.getEncoding();
466 }
467
468 }