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             outputDir.mkdirs();
242         }
243 
244         File descriptor = new File( outputDir, "application.xml" );
245 
246         final ApplicationXmlWriter writer = new ApplicationXmlWriter( javaEEVersion, encoding, generateModuleId );
247         final ApplicationXmlWriterContext context =
248             new ApplicationXmlWriterContext( descriptor, getModules(), buildSecurityRoles(), buildEnvEntries(),
249                                              buildEjbEntries(), buildResourceRefs(), displayName, description,
250                                              getActualLibraryDirectory(), applicationName,
251                                              initializeInOrder ).setApplicationId( applicationId );
252         writer.write( context );
253     }
254 
255     /**
256      * Generates the jboss deployment descriptor.
257      * 
258      * @throws EarPluginException if the configuration is invalid
259      */
260     protected void generateJbossDeploymentDescriptor()
261         throws EarPluginException
262     {
263         File outputDir = new File( generatedDescriptorLocation );
264         if ( !outputDir.exists() )
265         {
266             outputDir.mkdirs();
267         }
268 
269         File descriptor = new File( outputDir, "jboss-app.xml" );
270 
271         JbossAppXmlWriter writer = new JbossAppXmlWriter( encoding );
272         writer.write( descriptor, getJbossConfiguration(), getModules() );
273     }
274 
275     /**
276      * Builds the security roles based on the configuration.
277      * 
278      * @return a list of SecurityRole object(s)
279      * @throws EarPluginException if the configuration is invalid
280      */
281     private List<SecurityRole> buildSecurityRoles()
282         throws EarPluginException
283     {
284         final List<SecurityRole> result = new ArrayList<SecurityRole>();
285         if ( security == null )
286         {
287             return result;
288         }
289         final PlexusConfiguration[] securityRoles = security.getChildren( SecurityRole.SECURITY_ROLE );
290 
291         for ( PlexusConfiguration securityRole : securityRoles )
292         {
293             final String id = securityRole.getAttribute( SecurityRole.ID_ATTRIBUTE );
294             final String childRoleName = securityRole.getChild( SecurityRole.ROLE_NAME ).getValue();
295             final String childRoleNameId =
296                 securityRole.getChild( SecurityRole.ROLE_NAME ).getAttribute( SecurityRole.ID_ATTRIBUTE );
297             final String childDescription = securityRole.getChild( SecurityRole.DESCRIPTION ).getValue();
298             final String childDescriptionId =
299                 securityRole.getChild( SecurityRole.DESCRIPTION ).getAttribute( SecurityRole.ID_ATTRIBUTE );
300 
301             if ( childRoleName == null )
302             {
303                 throw new EarPluginException( "Invalid security-role configuration, role-name could not be null." );
304             }
305             else
306             {
307                 result.add( new SecurityRole( childRoleName, childRoleNameId, id, childDescription,
308                                               childDescriptionId ) );
309             }
310         }
311         return result;
312     }
313 
314     /**
315      * This help method was needed otherwise the interpolate method of interpolator will make an empty string of a
316      * {@code null} element which results in supplemental elements for env-entry.
317      * 
318      * @param interpolator The interpolator
319      * @param element The element
320      * @return The interpolated elements.
321      * @throws InterpolationException in case of an error.
322      */
323     private String interpolate( Interpolator interpolator, String element )
324         throws InterpolationException
325     {
326         if ( element == null )
327         {
328             return element;
329         }
330         else
331         {
332             return interpolator.interpolate( element );
333         }
334     }
335 
336     /**
337      * Builds the env-entries based on the configuration.
338      * 
339      * @return a list of EnvEntry object(s)
340      * @throws EarPluginException if the configuration is invalid
341      */
342     private List<EnvEntry> buildEnvEntries()
343         throws EarPluginException
344     {
345         final List<EnvEntry> result = new ArrayList<EnvEntry>();
346         if ( envEntries == null )
347         {
348             return result;
349         }
350         try
351         {
352             StringSearchInterpolator ssi = new StringSearchInterpolator();
353             ValueSource vs = new MapBasedValueSource( project.getProperties() );
354             ssi.addValueSource( vs );
355 
356             final PlexusConfiguration[] allEnvEntries = envEntries.getChildren( EnvEntry.ENV_ENTRY );
357 
358             getLog().debug( "buildEnvEntries: allEnvEntries:" + allEnvEntries );
359             getLog().debug( "buildEnvEntries: allEnvEntries size:" + allEnvEntries.length );
360             for ( PlexusConfiguration envEntry : allEnvEntries )
361             {
362                 // CHECKSTYLE_OFF: LineLength
363                 final String childDescription =
364                     interpolate( ssi, envEntry.getChild( EnvEntry.DESCRIPTION ).getValue() );
365                 final String childEnvEntryName =
366                     interpolate( ssi, envEntry.getChild( EnvEntry.ENV_ENTRY_NAME ).getValue() );
367                 final String childEnvEntryType =
368                     interpolate( ssi, envEntry.getChild( EnvEntry.ENV_ENTRY_TYPE ).getValue() );
369                 final String childEnvEntryValue =
370                     interpolate( ssi, envEntry.getChild( EnvEntry.ENV_ENTRY_VALUE ).getValue() );
371                 final String childEnvLookupNameValue =
372                     interpolate( ssi, envEntry.getChild( EnvEntry.ENV_LOOKUP_NAME ).getValue() );
373                 // CHECKSTYLE_ON: LineLength
374 
375                 try
376                 {
377                     result.add( new EnvEntry( childDescription, childEnvEntryName, childEnvEntryType,
378                                               childEnvEntryValue, childEnvLookupNameValue ) );
379                 }
380                 catch ( IllegalArgumentException e )
381                 {
382                     throw new EarPluginException( "Invalid env-entry [" + envEntry + "]", e );
383                 }
384             }
385             return result;
386         }
387         catch ( InterpolationException e )
388         {
389             throw new EarPluginException( "Interpolation exception:", e );
390         }
391 
392     }
393 
394     /**
395      * Builds the ejb-ref based on the configuration.
396      * 
397      * @return a list of EjbRef object(s)
398      * @throws EarPluginException if the configuration is invalid
399      */
400     private List<EjbRef> buildEjbEntries()
401         throws EarPluginException
402     {
403         final List<EjbRef> result = new ArrayList<EjbRef>();
404         if ( ejbRefs == null )
405         {
406             return result;
407         }
408         try
409         {
410             StringSearchInterpolator ssi = new StringSearchInterpolator();
411             ValueSource vs = new MapBasedValueSource( project.getProperties() );
412             ssi.addValueSource( vs );
413 
414             final PlexusConfiguration[] allEjbEntries = ejbRefs.getChildren( EjbRef.EJB_REF );
415 
416             for ( PlexusConfiguration ejbEntry : allEjbEntries )
417             {
418                 // CHECKSTYLE_OFF: LineLength
419                 final String childDescription =
420                     interpolate( ssi, ejbEntry.getChild( EnvEntry.DESCRIPTION ).getValue() );
421                 final String childEjbEntryName = interpolate( ssi, ejbEntry.getChild( EjbRef.EJB_NAME ).getValue() );
422                 final String childEjbEntryType = interpolate( ssi, ejbEntry.getChild( EjbRef.EJB_TYPE ).getValue() );
423                 final String childEjbLookupNameValue =
424                     interpolate( ssi, ejbEntry.getChild( EjbRef.EJB_LOOKUP_NAME ).getValue() );
425                 // CHECKSTYLE_ON: LineLength
426 
427                 try
428                 {
429                     result.add( new EjbRef( childDescription, childEjbEntryName, childEjbEntryType,
430                                             childEjbLookupNameValue ) );
431                 }
432                 catch ( IllegalArgumentException e )
433                 {
434                     throw new EarPluginException( "Invalid ejb-ref [" + ejbEntry + "]", e );
435                 }
436             }
437             return result;
438         }
439         catch ( InterpolationException e )
440         {
441             throw new EarPluginException( "Interpolation exception:", e );
442         }
443 
444     }
445 
446     /**
447      * Builds the <code>resource-ref</code> based on the configuration.
448      * 
449      * @return a list of ResourceRef object(s)
450      * @throws EarPluginException if the configuration is invalid
451      */
452     private List<ResourceRef> buildResourceRefs()
453         throws EarPluginException
454     {
455         final List<ResourceRef> result = new ArrayList<ResourceRef>();
456         if ( resourceRefs == null )
457         {
458             return result;
459         }
460         try
461         {
462             getLog().debug( "Resources found" );
463             StringSearchInterpolator ssi = new StringSearchInterpolator();
464             ValueSource vs = new MapBasedValueSource( project.getProperties() );
465             ssi.addValueSource( vs );
466 
467             // TODO: Check if this is a good idea hard code that here? Better idea?
468             final PlexusConfiguration[] allResourceRefEntries = resourceRefs.getChildren( "resourceRef" );
469 
470             getLog().debug( "allResourceRefEntries: " + allResourceRefEntries );
471             getLog().debug( "allResourceRefEntries length: " + allResourceRefEntries.length );
472             for ( PlexusConfiguration resEntry : allResourceRefEntries )
473             {
474                 getLog().debug( "Resources resEntry:" + resEntry.getName() );
475 
476                 // CHECKSTYLE_OFF: LineLength
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                 // CHECKSTYLE_ON: LineLength
484 
485                 try
486                 {
487                     result.add( new ResourceRef( childResRefName, childResType, childResRefAuth ) );
488                 }
489                 catch ( IllegalArgumentException e )
490                 {
491                     throw new EarPluginException( "Invalid resource-ref [" + resEntry + "]", e );
492                 }
493             }
494             return result;
495         }
496         catch ( InterpolationException e )
497         {
498             throw new EarPluginException( "Interpolation exception:", e );
499         }
500 
501     }
502 
503     /**
504      * Returns the value to use for the <tt>library-directory</tt> element, based on the library directory mode.
505      */
506     private String getActualLibraryDirectory()
507         throws EarPluginException
508     {
509         final String mode = libraryDirectoryMode == null ? DEFAULT : libraryDirectoryMode.toUpperCase();
510 
511         if ( DEFAULT.equals( mode ) )
512         {
513             return defaultLibBundleDir;
514         }
515         else if ( EMPTY.equals( mode ) )
516         {
517             return "";
518         }
519         else if ( NONE.equals( mode ) )
520         {
521             return null;
522         }
523         else
524         {
525             throw new EarPluginException( "Unsupported library directory mode [" + libraryDirectoryMode
526                 + "] Supported modes " + ( Arrays.asList( DEFAULT, EMPTY, NONE ) ) );
527         }
528     }
529 }