blob: 8ec99a05ef8b0d1e01b75cf5315447b245dbfb80 [file] [log] [blame]
package org.apache.maven.plugins.ear;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.plugins.ear.util.JavaEEVersion;
import org.codehaus.plexus.configuration.PlexusConfiguration;
import org.codehaus.plexus.interpolation.InterpolationException;
import org.codehaus.plexus.interpolation.Interpolator;
import org.codehaus.plexus.interpolation.MapBasedValueSource;
import org.codehaus.plexus.interpolation.StringSearchInterpolator;
import org.codehaus.plexus.interpolation.ValueSource;
import org.codehaus.plexus.util.FileUtils;
/**
* Generates the EAR deployment descriptor file(s).
*
* @author <a href="snicoll@apache.org">Stephane Nicoll</a>
*/
@Mojo( name = "generate-application-xml",
defaultPhase = LifecyclePhase.GENERATE_RESOURCES,
threadSafe = true,
requiresDependencyResolution = ResolutionScope.TEST )
public class GenerateApplicationXmlMojo
extends AbstractEarMojo
{
/**
* The DEFAULT library folder.
*/
public static final String DEFAULT = "DEFAULT";
/**
* The empty folder.
*/
public static final String EMPTY = "EMPTY";
/**
* The NONE not existent folder.
*/
public static final String NONE = "NONE";
/**
* Whether the application.xml should be generated or not.
*/
@Parameter( defaultValue = "true" )
private Boolean generateApplicationXml = Boolean.TRUE;
/**
* Whether a module ID should be generated if none is specified.
*/
@Parameter( defaultValue = "false" )
private Boolean generateModuleId = Boolean.FALSE;
/**
* Application name of the application to be used when the application.xml file is auto-generated. Since JavaEE6.
*/
@Parameter
private String applicationName;
/**
* Display name of the application to be used when the application.xml file is auto-generated.
*/
@Parameter( defaultValue = "${project.artifactId}" )
private String displayName;
/**
* Description of the application to be used when the application.xml file is auto-generated.
*/
@Parameter( defaultValue = "${project.description}" )
private String description;
/**
* Defines how the {@code library-directory} element should be written in the application.xml file.
* <p/>
* Three special values can be set:
* <ul>
* <li><code>DEFAULT</code> (default) generates a {@code library-directory} element with the value of the
* {@code defaultLibBundleDir} parameter</li>
* <li><code>EMPTY</code> generates an empty {@code library-directory} element. Per spec, this disables the
* scanning of jar files in the {@code lib} directory of the ear file</li>
* <li><code>NONE</code> does not write the library-directory element at all. A corner case that can be used in
* Oracle Weblogic to delegate the classloading to the container</li>
* </ul>
* <p/>
* Since JavaEE5.
*/
@Parameter( defaultValue = DEFAULT )
private String libraryDirectoryMode;
/**
* Defines the value of the initialize in order element to be used when the application.xml file is auto-generated.
* When set to true, modules must be initialized in the order they're listed in this deployment descriptor, with the
* exception of application client modules, which can be initialized in any order. If initialize-in-order is not set
* or set to false, the order of initialization is unspecified and may be product-dependent. Since JavaEE6.
*/
@Parameter
private Boolean initializeInOrder;
/**
* Defines the application id used when generating the deployment descriptor.
*
* @since 2.9
*/
@Parameter
private String applicationId;
/**
* The security-roles to be added to the auto-generated application.xml file.
*/
@Parameter
private PlexusConfiguration security;
/**
* The env-entries to be added to the auto-generated application.xml file. Since JavaEE6.
*/
@Parameter( alias = "env-entries" )
private PlexusConfiguration envEntries;
/**
* The {@code ejb-ref} entries.
*/
@Parameter( alias = "ejb-refs" )
private PlexusConfiguration ejbRefs;
/**
* The {@code resource-ref} entries.
*/
@Parameter
private PlexusConfiguration resourceRefs;
/**
* {@inheritDoc}
*/
public void execute()
throws MojoExecutionException, MojoFailureException
{
// Initializes ear modules
super.execute();
// Handle application.xml
if ( !generateApplicationXml )
{
getLog().debug( "Generation of application.xml is disabled" );
}
else
{
final JavaEEVersion javaEEVersion = JavaEEVersion.getJavaEEVersion( version );
// Generate deployment descriptor and copy it to the build directory
getLog().info( "Generating application.xml" );
try
{
generateStandardDeploymentDescriptor( javaEEVersion );
}
catch ( EarPluginException e )
{
throw new MojoExecutionException( "Failed to generate application.xml", e );
}
try
{
FileUtils.copyFileToDirectory( new File( generatedDescriptorLocation, "application.xml" ),
new File( getWorkDirectory(), "META-INF" ) );
}
catch ( IOException e )
{
throw new MojoExecutionException( "Unable to copy application.xml to final destination", e );
}
}
// Handle jboss-app.xml
if ( getJbossConfiguration() == null )
{
getLog().debug( "Generation of jboss-app.xml is disabled" );
}
else
{
// Generate deployment descriptor and copy it to the build directory
getLog().info( "Generating jboss-app.xml" );
try
{
generateJbossDeploymentDescriptor();
}
catch ( EarPluginException e )
{
throw new MojoExecutionException( "Failed to generate jboss-app.xml", e );
}
try
{
FileUtils.copyFileToDirectory( new File( generatedDescriptorLocation, "jboss-app.xml" ),
new File( getWorkDirectory(), "META-INF" ) );
}
catch ( IOException e )
{
throw new MojoExecutionException( "Unable to copy jboss-app.xml to final destination", e );
}
}
}
/**
* Generates the deployment descriptor.
*
* @param javaEEVersion {@link JavaEEVersion}
* @throws EarPluginException if the configuration is invalid
*/
protected void generateStandardDeploymentDescriptor( JavaEEVersion javaEEVersion )
throws EarPluginException
{
File outputDir = new File( generatedDescriptorLocation );
if ( !outputDir.exists() )
{
if ( !outputDir.mkdirs() )
{
throw new EarPluginException( "Error creating " + outputDir );
}
}
File descriptor = new File( outputDir, "application.xml" );
final ApplicationXmlWriter writer = new ApplicationXmlWriter( javaEEVersion, encoding, generateModuleId );
final ApplicationXmlWriterContext context =
new ApplicationXmlWriterContext( descriptor, getModules(), buildSecurityRoles(), buildEnvEntries(),
buildEjbEntries(), buildResourceRefs(), displayName, description,
getActualLibraryDirectory(), applicationName,
initializeInOrder ).setApplicationId( applicationId );
writer.write( context );
}
/**
* Generates the JBoss deployment descriptor.
*
* @throws EarPluginException if the configuration is invalid
*/
protected void generateJbossDeploymentDescriptor()
throws EarPluginException
{
File outputDir = new File( generatedDescriptorLocation );
if ( !outputDir.exists() )
{
if ( !outputDir.mkdirs() )
{
throw new EarPluginException( "Error creating " + outputDir );
}
}
File descriptor = new File( outputDir, "jboss-app.xml" );
JbossAppXmlWriter writer = new JbossAppXmlWriter( encoding );
writer.write( descriptor, getJbossConfiguration(), getModules() );
}
/**
* Builds the security roles based on the configuration.
*
* @return a list of SecurityRole object(s)
* @throws EarPluginException if the configuration is invalid
*/
private List<SecurityRole> buildSecurityRoles()
throws EarPluginException
{
final List<SecurityRole> result = new ArrayList<SecurityRole>();
if ( security == null )
{
return result;
}
final PlexusConfiguration[] securityRoles = security.getChildren( SecurityRole.SECURITY_ROLE );
for ( PlexusConfiguration securityRole : securityRoles )
{
final String id = securityRole.getAttribute( SecurityRole.ID_ATTRIBUTE );
final String childRoleName = securityRole.getChild( SecurityRole.ROLE_NAME ).getValue();
final String childRoleNameId =
securityRole.getChild( SecurityRole.ROLE_NAME ).getAttribute( SecurityRole.ID_ATTRIBUTE );
final String childDescription = securityRole.getChild( SecurityRole.DESCRIPTION ).getValue();
final String childDescriptionId =
securityRole.getChild( SecurityRole.DESCRIPTION ).getAttribute( SecurityRole.ID_ATTRIBUTE );
if ( childRoleName == null )
{
throw new EarPluginException( "Invalid security-role configuration, role-name could not be null." );
}
else
{
result.add( new SecurityRole( childRoleName, childRoleNameId, id, childDescription,
childDescriptionId ) );
}
}
return result;
}
/**
* This help method was needed otherwise the interpolate method of interpolator will make an empty string of a
* {@code null} element which results in supplemental elements for env-entry.
*
* @param interpolator The interpolator
* @param element The element
* @return The interpolated elements.
* @throws InterpolationException in case of an error.
*/
private String interpolate( Interpolator interpolator, String element )
throws InterpolationException
{
if ( element == null )
{
return element;
}
else
{
return interpolator.interpolate( element );
}
}
/**
* Builds the env-entries based on the configuration.
*
* @return a list of EnvEntry object(s)
* @throws EarPluginException if the configuration is invalid
*/
private List<EnvEntry> buildEnvEntries()
throws EarPluginException
{
final List<EnvEntry> result = new ArrayList<EnvEntry>();
if ( envEntries == null )
{
return result;
}
try
{
StringSearchInterpolator ssi = new StringSearchInterpolator();
ValueSource vs = new MapBasedValueSource( project.getProperties() );
ssi.addValueSource( vs );
final PlexusConfiguration[] allEnvEntries = envEntries.getChildren( EnvEntry.ENV_ENTRY );
getLog().debug( "buildEnvEntries: allEnvEntries:" + allEnvEntries );
getLog().debug( "buildEnvEntries: allEnvEntries size:" + allEnvEntries.length );
for ( PlexusConfiguration envEntry : allEnvEntries )
{
final String childDescription =
interpolate( ssi, envEntry.getChild( EnvEntry.DESCRIPTION ).getValue() );
final String childEnvEntryName =
interpolate( ssi, envEntry.getChild( EnvEntry.ENV_ENTRY_NAME ).getValue() );
final String childEnvEntryType =
interpolate( ssi, envEntry.getChild( EnvEntry.ENV_ENTRY_TYPE ).getValue() );
final String childEnvEntryValue =
interpolate( ssi, envEntry.getChild( EnvEntry.ENV_ENTRY_VALUE ).getValue() );
final String childEnvLookupNameValue =
interpolate( ssi, envEntry.getChild( EnvEntry.ENV_LOOKUP_NAME ).getValue() );
try
{
result.add( new EnvEntry( childDescription, childEnvEntryName, childEnvEntryType,
childEnvEntryValue, childEnvLookupNameValue ) );
}
catch ( IllegalArgumentException e )
{
throw new EarPluginException( "Invalid env-entry [" + envEntry + "]", e );
}
}
return result;
}
catch ( InterpolationException e )
{
throw new EarPluginException( "Interpolation exception:", e );
}
}
/**
* Builds the ejb-ref based on the configuration.
*
* @return a list of EjbRef object(s)
* @throws EarPluginException if the configuration is invalid
*/
private List<EjbRef> buildEjbEntries()
throws EarPluginException
{
final List<EjbRef> result = new ArrayList<EjbRef>();
if ( ejbRefs == null )
{
return result;
}
try
{
StringSearchInterpolator ssi = new StringSearchInterpolator();
ValueSource vs = new MapBasedValueSource( project.getProperties() );
ssi.addValueSource( vs );
final PlexusConfiguration[] allEjbEntries = ejbRefs.getChildren( EjbRef.EJB_REF );
for ( PlexusConfiguration ejbEntry : allEjbEntries )
{
final String childDescription =
interpolate( ssi, ejbEntry.getChild( EnvEntry.DESCRIPTION ).getValue() );
final String childEjbEntryName = interpolate( ssi, ejbEntry.getChild( EjbRef.EJB_NAME ).getValue() );
final String childEjbEntryType = interpolate( ssi, ejbEntry.getChild( EjbRef.EJB_TYPE ).getValue() );
final String childEjbLookupNameValue =
interpolate( ssi, ejbEntry.getChild( EjbRef.EJB_LOOKUP_NAME ).getValue() );
try
{
result.add( new EjbRef( childDescription, childEjbEntryName, childEjbEntryType,
childEjbLookupNameValue ) );
}
catch ( IllegalArgumentException e )
{
throw new EarPluginException( "Invalid ejb-ref [" + ejbEntry + "]", e );
}
}
return result;
}
catch ( InterpolationException e )
{
throw new EarPluginException( "Interpolation exception:", e );
}
}
/**
* Builds the <code>resource-ref</code> based on the configuration.
*
* @return a list of ResourceRef object(s)
* @throws EarPluginException if the configuration is invalid
*/
private List<ResourceRef> buildResourceRefs()
throws EarPluginException
{
final List<ResourceRef> result = new ArrayList<ResourceRef>();
if ( resourceRefs == null )
{
return result;
}
try
{
getLog().debug( "Resources found" );
StringSearchInterpolator ssi = new StringSearchInterpolator();
ValueSource vs = new MapBasedValueSource( project.getProperties() );
ssi.addValueSource( vs );
// TODO: Check if this is a good idea hard code that here? Better idea?
final PlexusConfiguration[] allResourceRefEntries = resourceRefs.getChildren( "resourceRef" );
getLog().debug( "allResourceRefEntries: " + allResourceRefEntries );
getLog().debug( "allResourceRefEntries length: " + allResourceRefEntries.length );
for ( PlexusConfiguration resEntry : allResourceRefEntries )
{
getLog().debug( "Resources resEntry:" + resEntry.getName() );
final String childResRefName =
interpolate( ssi, resEntry.getChild( ResourceRef.RESOURCE_REF_NAME ).getValue() );
final String childResType =
interpolate( ssi, resEntry.getChild( ResourceRef.RESOURCE_TYPE ).getValue() );
final String childResRefAuth =
interpolate( ssi, resEntry.getChild( ResourceRef.RESOURCE_AUTH ).getValue() );
final String childResRefLookupName =
interpolate( ssi, resEntry.getChild( ResourceRef.LOOKUP_NAME ).getValue() );
try
{
result.add(
new ResourceRef( childResRefName, childResType, childResRefAuth, childResRefLookupName ) );
}
catch ( IllegalArgumentException e )
{
throw new EarPluginException( "Invalid resource-ref [" + resEntry + "]", e );
}
}
return result;
}
catch ( InterpolationException e )
{
throw new EarPluginException( "Interpolation exception:", e );
}
}
/**
* Returns the value to use for the {@code library-directory} element, based on the library directory mode.
*/
private String getActualLibraryDirectory()
throws EarPluginException
{
final String mode = libraryDirectoryMode == null ? DEFAULT : libraryDirectoryMode.toUpperCase();
if ( DEFAULT.equals( mode ) )
{
return defaultLibBundleDir;
}
else if ( EMPTY.equals( mode ) )
{
return "";
}
else if ( NONE.equals( mode ) )
{
return null;
}
else
{
throw new EarPluginException( "Unsupported library directory mode [" + libraryDirectoryMode
+ "] Supported modes " + ( Arrays.asList( DEFAULT, EMPTY, NONE ) ) );
}
}
}