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.io.File;
23  import java.io.IOException;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.List;
27  
28  import org.apache.maven.plugin.MojoExecutionException;
29  import org.apache.maven.plugin.MojoFailureException;
30  import org.apache.maven.plugins.annotations.LifecyclePhase;
31  import org.apache.maven.plugins.annotations.Mojo;
32  import org.apache.maven.plugins.annotations.Parameter;
33  import org.apache.maven.plugins.annotations.ResolutionScope;
34  import org.apache.maven.plugins.ear.util.JavaEEVersion;
35  import org.codehaus.plexus.configuration.PlexusConfiguration;
36  import org.codehaus.plexus.interpolation.InterpolationException;
37  import org.codehaus.plexus.interpolation.Interpolator;
38  import org.codehaus.plexus.interpolation.MapBasedValueSource;
39  import org.codehaus.plexus.interpolation.StringSearchInterpolator;
40  import org.codehaus.plexus.interpolation.ValueSource;
41  import org.codehaus.plexus.util.FileUtils;
42  
43  /**
44   * Generates the EAR deployment descriptor file(s).
45   * 
46   * @author <a href="snicoll@apache.org">Stephane Nicoll</a>
47   */
48  @Mojo( name = "generate-application-xml",
49         defaultPhase = LifecyclePhase.GENERATE_RESOURCES,
50         threadSafe = true,
51         requiresDependencyResolution = ResolutionScope.TEST )
52  public class GenerateApplicationXmlMojo
53      extends AbstractEarMojo
54  {
55  
56      /**
57       * The DEFAULT library folder.
58       */
59      public static final String DEFAULT = "DEFAULT";
60  
61      /**
62       * The empty folder.
63       */
64      public static final String EMPTY = "EMPTY";
65  
66      /**
67       * The NONE not existent folder.
68       */
69      public static final String NONE = "NONE";
70  
71      /**
72       * Whether the application.xml should be generated or not.
73       */
74      @Parameter( defaultValue = "true" )
75      private Boolean generateApplicationXml = Boolean.TRUE;
76  
77      /**
78       * Whether a module ID should be generated if none is specified.
79       */
80      @Parameter( defaultValue = "false" )
81      private Boolean generateModuleId = Boolean.FALSE;
82  
83      /**
84       * Application name of the application to be used when the application.xml file is auto-generated. Since JavaEE6.
85       */
86      @Parameter
87      private String applicationName;
88  
89      /**
90       * Display name of the application to be used when the application.xml file is auto-generated.
91       */
92      @Parameter( defaultValue = "${project.artifactId}" )
93      private String displayName;
94  
95      /**
96       * Description of the application to be used when the application.xml file is auto-generated.
97       */
98      @Parameter( defaultValue = "${project.description}" )
99      private String description;
100 
101     /**
102      * Defines how the {@code library-directory} element should be written in the application.xml file.
103      * <p/>
104      * Three special values can be set:
105      * <ul>
106      * <li><code>DEFAULT</code> (default) generates a {@code library-directory} element with the value of the
107      * {@code defaultLibBundleDir} parameter</li>
108      * <li><code>EMPTY</code> generates an empty {@code library-directory} element. Per spec, this disables the
109      * scanning of jar files in the {@code lib} directory of the ear file</li>
110      * <li><code>NONE</code> does not write the library-directory element at all. A corner case that can be used in
111      * Oracle Weblogic to delegate the classloading to the container</li>
112      * </ul>
113      * <p/>
114      * Since JavaEE5.
115      */
116     @Parameter( defaultValue = DEFAULT )
117     private String libraryDirectoryMode;
118 
119     /**
120      * Defines the value of the initialize in order element to be used when the application.xml file is auto-generated.
121      * When set to true, modules must be initialized in the order they're listed in this deployment descriptor, with the
122      * exception of application client modules, which can be initialized in any order. If initialize-in-order is not set
123      * or set to false, the order of initialization is unspecified and may be product-dependent. Since JavaEE6.
124      */
125     @Parameter
126     private Boolean initializeInOrder;
127 
128     /**
129      * Defines the application id used when generating the deployment descriptor.
130      * 
131      * @since 2.9
132      */
133     @Parameter
134     private String applicationId;
135 
136     /**
137      * The security-roles to be added to the auto-generated application.xml file.
138      */
139     @Parameter
140     private PlexusConfiguration security;
141 
142     /**
143      * The env-entries to be added to the auto-generated application.xml file. Since JavaEE6.
144      */
145     @Parameter( alias = "env-entries" )
146     private PlexusConfiguration envEntries;
147 
148     /**
149      * The {@code ejb-ref} entries.
150      */
151     @Parameter( alias = "ejb-refs" )
152     private PlexusConfiguration ejbRefs;
153 
154     /**
155      * The {@code resource-ref} entries.
156      */
157     @Parameter
158     private PlexusConfiguration resourceRefs;
159 
160     /**
161      * {@inheritDoc}
162      */
163     public void execute()
164         throws MojoExecutionException, MojoFailureException
165     {
166         // Initializes ear modules
167         super.execute();
168 
169         // Handle application.xml
170         if ( !generateApplicationXml )
171         {
172             getLog().debug( "Generation of application.xml is disabled" );
173         }
174         else
175         {
176             final JavaEEVersion javaEEVersion = JavaEEVersion.getJavaEEVersion( version );
177 
178             // Generate deployment descriptor and copy it to the build directory
179             getLog().info( "Generating application.xml" );
180             try
181             {
182                 generateStandardDeploymentDescriptor( javaEEVersion );
183             }
184             catch ( EarPluginException e )
185             {
186                 throw new MojoExecutionException( "Failed to generate application.xml", e );
187             }
188 
189             try
190             {
191                 FileUtils.copyFileToDirectory( new File( generatedDescriptorLocation, "application.xml" ),
192                                                new File( getWorkDirectory(), "META-INF" ) );
193             }
194             catch ( IOException e )
195             {
196                 throw new MojoExecutionException( "Unable to copy application.xml to final destination", e );
197             }
198         }
199 
200         // Handle jboss-app.xml
201         if ( getJbossConfiguration() == null )
202         {
203             getLog().debug( "Generation of jboss-app.xml is disabled" );
204         }
205         else
206         {
207             // Generate deployment descriptor and copy it to the build directory
208             getLog().info( "Generating jboss-app.xml" );
209             try
210             {
211                 generateJbossDeploymentDescriptor();
212             }
213             catch ( EarPluginException e )
214             {
215                 throw new MojoExecutionException( "Failed to generate jboss-app.xml", e );
216             }
217 
218             try
219             {
220                 FileUtils.copyFileToDirectory( new File( generatedDescriptorLocation, "jboss-app.xml" ),
221                                                new File( getWorkDirectory(), "META-INF" ) );
222             }
223             catch ( IOException e )
224             {
225                 throw new MojoExecutionException( "Unable to copy jboss-app.xml to final destination", e );
226             }
227         }
228     }
229 
230     /**
231      * Generates the deployment descriptor.
232      * 
233      * @param javaEEVersion {@link JavaEEVersion}
234      * @throws EarPluginException if the configuration is invalid
235      */
236     protected void generateStandardDeploymentDescriptor( JavaEEVersion javaEEVersion )
237         throws EarPluginException
238     {
239         File outputDir = new File( generatedDescriptorLocation );
240         if ( !outputDir.exists() )
241         {
242             if ( !outputDir.mkdirs() )
243             {
244                 throw new EarPluginException( "Error creating " + outputDir );
245             }
246         }
247 
248         File descriptor = new File( outputDir, "application.xml" );
249 
250         final ApplicationXmlWriter writer = new ApplicationXmlWriter( javaEEVersion, encoding, generateModuleId );
251         final ApplicationXmlWriterContext context =
252             new ApplicationXmlWriterContext( descriptor, getModules(), buildSecurityRoles(), buildEnvEntries(),
253                                              buildEjbEntries(), buildResourceRefs(), displayName, description,
254                                              getActualLibraryDirectory(), applicationName,
255                                              initializeInOrder ).setApplicationId( applicationId );
256         writer.write( context );
257     }
258 
259     /**
260      * Generates the JBoss deployment descriptor.
261      * 
262      * @throws EarPluginException if the configuration is invalid
263      */
264     protected void generateJbossDeploymentDescriptor()
265         throws EarPluginException
266     {
267         File outputDir = new File( generatedDescriptorLocation );
268         if ( !outputDir.exists() )
269         {
270             if ( !outputDir.mkdirs() )
271             {
272                 throw new EarPluginException( "Error creating " + outputDir );
273             }
274         }
275 
276         File descriptor = new File( outputDir, "jboss-app.xml" );
277 
278         JbossAppXmlWriter writer = new JbossAppXmlWriter( encoding );
279         writer.write( descriptor, getJbossConfiguration(), getModules() );
280     }
281 
282     /**
283      * Builds the security roles based on the configuration.
284      * 
285      * @return a list of SecurityRole object(s)
286      * @throws EarPluginException if the configuration is invalid
287      */
288     private List<SecurityRole> buildSecurityRoles()
289         throws EarPluginException
290     {
291         final List<SecurityRole> result = new ArrayList<SecurityRole>();
292         if ( security == null )
293         {
294             return result;
295         }
296         final PlexusConfiguration[] securityRoles = security.getChildren( SecurityRole.SECURITY_ROLE );
297 
298         for ( PlexusConfiguration securityRole : securityRoles )
299         {
300             final String id = securityRole.getAttribute( SecurityRole.ID_ATTRIBUTE );
301             final String childRoleName = securityRole.getChild( SecurityRole.ROLE_NAME ).getValue();
302             final String childRoleNameId =
303                 securityRole.getChild( SecurityRole.ROLE_NAME ).getAttribute( SecurityRole.ID_ATTRIBUTE );
304             final String childDescription = securityRole.getChild( SecurityRole.DESCRIPTION ).getValue();
305             final String childDescriptionId =
306                 securityRole.getChild( SecurityRole.DESCRIPTION ).getAttribute( SecurityRole.ID_ATTRIBUTE );
307 
308             if ( childRoleName == null )
309             {
310                 throw new EarPluginException( "Invalid security-role configuration, role-name could not be null." );
311             }
312             else
313             {
314                 result.add( new SecurityRole( childRoleName, childRoleNameId, id, childDescription,
315                                               childDescriptionId ) );
316             }
317         }
318         return result;
319     }
320 
321     /**
322      * This help method was needed otherwise the interpolate method of interpolator will make an empty string of a
323      * {@code null} element which results in supplemental elements for env-entry.
324      * 
325      * @param interpolator The interpolator
326      * @param element The element
327      * @return The interpolated elements.
328      * @throws InterpolationException in case of an error.
329      */
330     private String interpolate( Interpolator interpolator, String element )
331         throws InterpolationException
332     {
333         if ( element == null )
334         {
335             return element;
336         }
337         else
338         {
339             return interpolator.interpolate( element );
340         }
341     }
342 
343     /**
344      * Builds the env-entries based on the configuration.
345      * 
346      * @return a list of EnvEntry object(s)
347      * @throws EarPluginException if the configuration is invalid
348      */
349     private List<EnvEntry> buildEnvEntries()
350         throws EarPluginException
351     {
352         final List<EnvEntry> result = new ArrayList<EnvEntry>();
353         if ( envEntries == null )
354         {
355             return result;
356         }
357         try
358         {
359             StringSearchInterpolator ssi = new StringSearchInterpolator();
360             ValueSource vs = new MapBasedValueSource( project.getProperties() );
361             ssi.addValueSource( vs );
362 
363             final PlexusConfiguration[] allEnvEntries = envEntries.getChildren( EnvEntry.ENV_ENTRY );
364 
365             getLog().debug( "buildEnvEntries: allEnvEntries size:" + allEnvEntries.length );
366             for ( PlexusConfiguration envEntry : allEnvEntries )
367             {
368                 final String childDescription =
369                     interpolate( ssi, envEntry.getChild( EnvEntry.DESCRIPTION ).getValue() );
370                 final String childEnvEntryName =
371                     interpolate( ssi, envEntry.getChild( EnvEntry.ENV_ENTRY_NAME ).getValue() );
372                 final String childEnvEntryType =
373                     interpolate( ssi, envEntry.getChild( EnvEntry.ENV_ENTRY_TYPE ).getValue() );
374                 final String childEnvEntryValue =
375                     interpolate( ssi, envEntry.getChild( EnvEntry.ENV_ENTRY_VALUE ).getValue() );
376                 final String childEnvLookupNameValue =
377                     interpolate( ssi, envEntry.getChild( EnvEntry.ENV_LOOKUP_NAME ).getValue() );
378 
379                 try
380                 {
381                     result.add( new EnvEntry( childDescription, childEnvEntryName, childEnvEntryType,
382                                               childEnvEntryValue, childEnvLookupNameValue ) );
383                 }
384                 catch ( IllegalArgumentException e )
385                 {
386                     throw new EarPluginException( "Invalid env-entry [" + envEntry + "]", e );
387                 }
388             }
389             return result;
390         }
391         catch ( InterpolationException e )
392         {
393             throw new EarPluginException( "Interpolation exception:", e );
394         }
395 
396     }
397 
398     /**
399      * Builds the ejb-ref based on the configuration.
400      * 
401      * @return a list of EjbRef object(s)
402      * @throws EarPluginException if the configuration is invalid
403      */
404     private List<EjbRef> buildEjbEntries()
405         throws EarPluginException
406     {
407         final List<EjbRef> result = new ArrayList<EjbRef>();
408         if ( ejbRefs == null )
409         {
410             return result;
411         }
412         try
413         {
414             StringSearchInterpolator ssi = new StringSearchInterpolator();
415             ValueSource vs = new MapBasedValueSource( project.getProperties() );
416             ssi.addValueSource( vs );
417 
418             final PlexusConfiguration[] allEjbEntries = ejbRefs.getChildren( EjbRef.EJB_REF );
419 
420             for ( PlexusConfiguration ejbEntry : allEjbEntries )
421             {
422                 final String childDescription =
423                     interpolate( ssi, ejbEntry.getChild( EnvEntry.DESCRIPTION ).getValue() );
424                 final String childEjbEntryName = interpolate( ssi, ejbEntry.getChild( EjbRef.EJB_NAME ).getValue() );
425                 final String childEjbEntryType = interpolate( ssi, ejbEntry.getChild( EjbRef.EJB_TYPE ).getValue() );
426                 final String childEjbLookupNameValue =
427                     interpolate( ssi, ejbEntry.getChild( EjbRef.EJB_LOOKUP_NAME ).getValue() );
428 
429                 try
430                 {
431                     result.add( new EjbRef( childDescription, childEjbEntryName, childEjbEntryType,
432                                             childEjbLookupNameValue ) );
433                 }
434                 catch ( IllegalArgumentException e )
435                 {
436                     throw new EarPluginException( "Invalid ejb-ref [" + ejbEntry + "]", e );
437                 }
438             }
439             return result;
440         }
441         catch ( InterpolationException e )
442         {
443             throw new EarPluginException( "Interpolation exception:", e );
444         }
445 
446     }
447 
448     /**
449      * Builds the <code>resource-ref</code> based on the configuration.
450      * 
451      * @return a list of ResourceRef object(s)
452      * @throws EarPluginException if the configuration is invalid
453      */
454     private List<ResourceRef> buildResourceRefs()
455         throws EarPluginException
456     {
457         final List<ResourceRef> result = new ArrayList<ResourceRef>();
458         if ( resourceRefs == null )
459         {
460             return result;
461         }
462         try
463         {
464             getLog().debug( "Resources found" );
465             StringSearchInterpolator ssi = new StringSearchInterpolator();
466             ValueSource vs = new MapBasedValueSource( project.getProperties() );
467             ssi.addValueSource( vs );
468 
469             // TODO: Check if this is a good idea hard code that here? Better idea?
470             final PlexusConfiguration[] allResourceRefEntries = resourceRefs.getChildren( "resourceRef" );
471 
472             getLog().debug( "allResourceRefEntries length: " + allResourceRefEntries.length );
473             for ( PlexusConfiguration resEntry : allResourceRefEntries )
474             {
475                 getLog().debug( "Resources resEntry:" + resEntry.getName() );
476 
477                 final String childResRefName =
478                     interpolate( ssi, resEntry.getChild( ResourceRef.RESOURCE_REF_NAME ).getValue() );
479                 final String childResType =
480                     interpolate( ssi, resEntry.getChild( ResourceRef.RESOURCE_TYPE ).getValue() );
481                 final String childResRefAuth =
482                     interpolate( ssi, resEntry.getChild( ResourceRef.RESOURCE_AUTH ).getValue() );
483                 final String childResRefLookupName =
484                     interpolate( ssi, resEntry.getChild( ResourceRef.LOOKUP_NAME ).getValue() );
485 
486                 try
487                 {
488                     result.add(
489                             new ResourceRef( childResRefName, childResType, childResRefAuth, childResRefLookupName ) );
490                 }
491                 catch ( IllegalArgumentException e )
492                 {
493                     throw new EarPluginException( "Invalid resource-ref [" + resEntry + "]", e );
494                 }
495             }
496             return result;
497         }
498         catch ( InterpolationException e )
499         {
500             throw new EarPluginException( "Interpolation exception:", e );
501         }
502 
503     }
504 
505     /**
506      * Returns the value to use for the {@code library-directory} element, based on the library directory mode.
507      */
508     private String getActualLibraryDirectory()
509         throws EarPluginException
510     {
511         final String mode = libraryDirectoryMode == null ? DEFAULT : libraryDirectoryMode.toUpperCase();
512 
513         if ( DEFAULT.equals( mode ) )
514         {
515             return defaultLibBundleDir;
516         }
517         else if ( EMPTY.equals( mode ) )
518         {
519             return "";
520         }
521         else if ( NONE.equals( mode ) )
522         {
523             return null;
524         }
525         else
526         {
527             throw new EarPluginException( "Unsupported library directory mode [" + libraryDirectoryMode
528                 + "] Supported modes " + ( Arrays.asList( DEFAULT, EMPTY, NONE ) ) );
529         }
530     }
531 }