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:" + allEnvEntries );
366             getLog().debug( "buildEnvEntries: allEnvEntries size:" + allEnvEntries.length );
367             for ( PlexusConfiguration envEntry : allEnvEntries )
368             {
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 
380                 try
381                 {
382                     result.add( new EnvEntry( childDescription, childEnvEntryName, childEnvEntryType,
383                                               childEnvEntryValue, childEnvLookupNameValue ) );
384                 }
385                 catch ( IllegalArgumentException e )
386                 {
387                     throw new EarPluginException( "Invalid env-entry [" + envEntry + "]", e );
388                 }
389             }
390             return result;
391         }
392         catch ( InterpolationException e )
393         {
394             throw new EarPluginException( "Interpolation exception:", e );
395         }
396 
397     }
398 
399     /**
400      * Builds the ejb-ref based on the configuration.
401      * 
402      * @return a list of EjbRef object(s)
403      * @throws EarPluginException if the configuration is invalid
404      */
405     private List<EjbRef> buildEjbEntries()
406         throws EarPluginException
407     {
408         final List<EjbRef> result = new ArrayList<EjbRef>();
409         if ( ejbRefs == null )
410         {
411             return result;
412         }
413         try
414         {
415             StringSearchInterpolator ssi = new StringSearchInterpolator();
416             ValueSource vs = new MapBasedValueSource( project.getProperties() );
417             ssi.addValueSource( vs );
418 
419             final PlexusConfiguration[] allEjbEntries = ejbRefs.getChildren( EjbRef.EJB_REF );
420 
421             for ( PlexusConfiguration ejbEntry : allEjbEntries )
422             {
423                 final String childDescription =
424                     interpolate( ssi, ejbEntry.getChild( EnvEntry.DESCRIPTION ).getValue() );
425                 final String childEjbEntryName = interpolate( ssi, ejbEntry.getChild( EjbRef.EJB_NAME ).getValue() );
426                 final String childEjbEntryType = interpolate( ssi, ejbEntry.getChild( EjbRef.EJB_TYPE ).getValue() );
427                 final String childEjbLookupNameValue =
428                     interpolate( ssi, ejbEntry.getChild( EjbRef.EJB_LOOKUP_NAME ).getValue() );
429 
430                 try
431                 {
432                     result.add( new EjbRef( childDescription, childEjbEntryName, childEjbEntryType,
433                                             childEjbLookupNameValue ) );
434                 }
435                 catch ( IllegalArgumentException e )
436                 {
437                     throw new EarPluginException( "Invalid ejb-ref [" + ejbEntry + "]", e );
438                 }
439             }
440             return result;
441         }
442         catch ( InterpolationException e )
443         {
444             throw new EarPluginException( "Interpolation exception:", e );
445         }
446 
447     }
448 
449     /**
450      * Builds the <code>resource-ref</code> based on the configuration.
451      * 
452      * @return a list of ResourceRef object(s)
453      * @throws EarPluginException if the configuration is invalid
454      */
455     private List<ResourceRef> buildResourceRefs()
456         throws EarPluginException
457     {
458         final List<ResourceRef> result = new ArrayList<ResourceRef>();
459         if ( resourceRefs == null )
460         {
461             return result;
462         }
463         try
464         {
465             getLog().debug( "Resources found" );
466             StringSearchInterpolator ssi = new StringSearchInterpolator();
467             ValueSource vs = new MapBasedValueSource( project.getProperties() );
468             ssi.addValueSource( vs );
469 
470             // TODO: Check if this is a good idea hard code that here? Better idea?
471             final PlexusConfiguration[] allResourceRefEntries = resourceRefs.getChildren( "resourceRef" );
472 
473             getLog().debug( "allResourceRefEntries: " + allResourceRefEntries );
474             getLog().debug( "allResourceRefEntries length: " + allResourceRefEntries.length );
475             for ( PlexusConfiguration resEntry : allResourceRefEntries )
476             {
477                 getLog().debug( "Resources resEntry:" + resEntry.getName() );
478 
479                 final String childResRefName =
480                     interpolate( ssi, resEntry.getChild( ResourceRef.RESOURCE_REF_NAME ).getValue() );
481                 final String childResType =
482                     interpolate( ssi, resEntry.getChild( ResourceRef.RESOURCE_TYPE ).getValue() );
483                 final String childResRefAuth =
484                     interpolate( ssi, resEntry.getChild( ResourceRef.RESOURCE_AUTH ).getValue() );
485                 final String childResRefLookupName =
486                     interpolate( ssi, resEntry.getChild( ResourceRef.LOOKUP_NAME ).getValue() );
487 
488                 try
489                 {
490                     result.add(
491                             new ResourceRef( childResRefName, childResType, childResRefAuth, childResRefLookupName ) );
492                 }
493                 catch ( IllegalArgumentException e )
494                 {
495                     throw new EarPluginException( "Invalid resource-ref [" + resEntry + "]", e );
496                 }
497             }
498             return result;
499         }
500         catch ( InterpolationException e )
501         {
502             throw new EarPluginException( "Interpolation exception:", e );
503         }
504 
505     }
506 
507     /**
508      * Returns the value to use for the {@code library-directory} element, based on the library directory mode.
509      */
510     private String getActualLibraryDirectory()
511         throws EarPluginException
512     {
513         final String mode = libraryDirectoryMode == null ? DEFAULT : libraryDirectoryMode.toUpperCase();
514 
515         if ( DEFAULT.equals( mode ) )
516         {
517             return defaultLibBundleDir;
518         }
519         else if ( EMPTY.equals( mode ) )
520         {
521             return "";
522         }
523         else if ( NONE.equals( mode ) )
524         {
525             return null;
526         }
527         else
528         {
529             throw new EarPluginException( "Unsupported library directory mode [" + libraryDirectoryMode
530                 + "] Supported modes " + ( Arrays.asList( DEFAULT, EMPTY, NONE ) ) );
531         }
532     }
533 }