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