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