All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.codehaus.mojo.webstart.JnlpDownloadServletMojo Maven / Gradle / Ivy

/*
 * Copyright 2001-2005 The Apache Software Foundation.
 *
 * Licensed 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.
 */
package org.codehaus.mojo.webstart;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;
import org.codehaus.mojo.webstart.generator.GeneratorExtraConfig;
import org.codehaus.mojo.webstart.generator.JarResourcesGenerator;
import org.codehaus.mojo.webstart.generator.VersionXmlGenerator;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.StringUtils;

/**
 * This MOJO is tailored for use within a Maven web application project that uses 
 * the JnlpDownloadServlet to serve up the JNLP application.
 * 
 * @author Kevin Stembridge
 * @since 1.0-alpha-2
 * @version $Id:$
 * @goal jnlp-download-servlet
 * @requiresDependencyResolution runtime
 * @requiresProject
 * @inheritedByDefault true
 */
public class JnlpDownloadServletMojo extends AbstractBaseJnlpMojo
{
    
    /**
     * @parameter default-value="${project}"
     * @required
     * @readonly
     */
    private MavenProject project;

    /**
     * The project's artifact metadata source, used to resolve transitive dependencies.
     * @component
     * @required
     * @readonly
     */
    private ArtifactMetadataSource artifactMetadataSource;
    
    /**
     * The name of the directory into which the jnlp file and other 
     * artifacts will be stored after processing. This directory will be created 
     * directly within the root of the WAR produced by the enclosing project.
     * 
     * @parameter default-value="webstart"
     */
    private String outputDirectoryName;
    
    /**
     * The collection of JnlpFile configuration elements. Each one represents a
     * JNLP file that is to be generated and deployed within the enclosing 
     * project's WAR artifact. At least one JnlpFile must be specified.
     * 
     * @parameter 
     * @required
     */
    private List/*JnlpFile*/ jnlpFiles;

    /**
     * The configurable collection of jars that are common to all jnlpFile elements declared in 
     * plugin configuration. These jars will be output as jar elements in the resources section of 
     * every generated JNLP file and bundled into the specified output directory of the artifact 
     * produced by the project.
     * 
     * @parameter
     */
    private List/*JarResource*/ commonJarResources;

    /**
     * Creates a new uninitialized {@code JnlpDownloadServletMojo}.
     */
    public JnlpDownloadServletMojo()
    {
        // do nothing
    }

    /**
     * {@inheritDoc}
     */
    public void execute() throws MojoExecutionException, MojoFailureException
    {
        
        checkConfiguration();
        
        try
        {
            copyResources( getResourcesDirectory(), getWorkDirectory() );
        }
        catch ( IOException e )
        {
            throw new MojoExecutionException("An error occurred attempting to copy " 
                                             + "resources to the working directory.", e);
        }
        
        if (this.commonJarResources != null )
        {
            retrieveJarResources( this.commonJarResources );
        }
        
        for (Iterator itr = this.jnlpFiles.iterator(); itr.hasNext(); )
        {
            JnlpFile jnlpFile = (JnlpFile) itr.next();
            retrieveJarResources( jnlpFile.getJarResources() );
        }
        
        signOrRenameJars();
        packJars();
        
        for ( Iterator itr = this.jnlpFiles.iterator(); itr.hasNext(); )
        {
            generateJnlpFile( (JnlpFile) itr.next() );
        }
        
        generateVersionXml();
        copyWorkingDirToOutputDir();
        
    }
    
    /**
     * Confirms that all plugin configuration provided by the user 
     * in the pom.xml file is valid.
     *
     * @throws MojoExecutionException if any user configuration is invalid. 
     */
    private void checkConfiguration() throws MojoExecutionException 
    {
        
        if ( this.jnlpFiles.isEmpty() )
        {
            throw new MojoExecutionException( 
                    "Configuration error: At least one  element must be specified" );
        }

        if ( this.jnlpFiles.size() == 1 && StringUtils.isEmpty( ((JnlpFile) this.jnlpFiles.get(0)).getOutputFilename() ) )
        {
            getLog().debug( "Jnlp output file name not specified in single set of jnlpFiles. Using default output file name: launch.jnlp." );
            ((JnlpFile) this.jnlpFiles.get(0)).setOutputFilename( "launch.jnlp" );
        }
         
        for ( Iterator itr = this.jnlpFiles.iterator(); itr.hasNext(); ) 
        {
            checkJnlpFileConfiguration( (JnlpFile) itr.next() );
        }
        
        checkForDuplicateJarResources();
        checkCommonJarResources();
        checkForUniqueJnlpFilenames();
        checkPack200();
        
    }
    
    /**
     * Checks the validity of a single jnlpFile configuration element.
     *
     * @param jnlpFile The configuration element to be checked.
     * @throws MojoExecutionException if the config element is invalid.
     */
    private void checkJnlpFileConfiguration( JnlpFile jnlpFile ) throws MojoExecutionException 
    {
        
        if ( StringUtils.isEmpty( jnlpFile.getOutputFilename() ) )
        {
            throw new MojoExecutionException( 
                    "Configuration error: An outputFilename must be specified for each jnlpFile element" );
        }
        
        if ( jnlpFile.getTemplateFilename() == null )
        {
            getLog().info( "No templateFilename found for " + jnlpFile.getOutputFilename()
                          + ". Will use the default template." );
        }
        else
        {        
          File templateFile = new File( getTemplateDirectory(), jnlpFile.getTemplateFilename() );
        
          if ( !templateFile.isFile() )
          {
              throw new MojoExecutionException( "The specified JNLP template does not exist: [" + templateFile + "]" );
          }
        }
        
        checkJnlpJarResources( jnlpFile );
        
    }
    
    /**
     * Checks the collection of jarResources configured for a given jnlpFile element.
     *
     * @param jnlpFile The configuration element whose jarResources are to be checked.
     * @throws MojoExecutionException if any config is invalid.
     */
    private void checkJnlpJarResources( JnlpFile jnlpFile ) throws MojoExecutionException
    {
        
        List jnlpJarResources = jnlpFile.getJarResources();
        
        if ( jnlpJarResources == null || jnlpJarResources.isEmpty() )
        {
            throw new MojoExecutionException(
                    "Configuration error: A non-empty  element must be specified in the plugin "
                    + "configuration for the JNLP file named [" + jnlpFile.getOutputFilename() + "]" );
        }

        Iterator itr = jnlpJarResources.iterator();
        List/*JarResource*/ jarsWithMainClass = new ArrayList();

        while ( itr.hasNext() )
        {
            JarResource jarResource = (JarResource) itr.next();
            
            checkMandatoryJarResourceFields( jarResource );
            
            if ( jarResource.getMainClass() != null )
            {
                jnlpFile.setMainClass( jarResource.getMainClass() );
                jarsWithMainClass.add( jarResource );
            }

        }

        if ( jarsWithMainClass.isEmpty() )
        {
            throw new MojoExecutionException(
                    "Configuration error: Exactly one  element must "
                    + "be declared with a  element in the configuration for JNLP file ["
                    + jnlpFile.getOutputFilename() + "]" );
        }

        if ( jarsWithMainClass.size() > 1 )
        {
            throw new MojoExecutionException(
                    "Configuration error: More than one  element has been declared "
                    + "with a  element in the configuration for JNLP file ["
                    + jnlpFile.getOutputFilename() + "]" );
        }

    }
    
    /**
     * Checks that any jarResources defined in the jnlpFile elements are not also defined in 
     * commonJarResources.
     * @throws MojoExecutionException if a duplicate is found. 
     */
    private void checkForDuplicateJarResources() throws MojoExecutionException 
    {
        
        if ( this.commonJarResources == null || this.commonJarResources.isEmpty() )
        {
            return;
        }
        
        for ( Iterator jnlpFileItr = this.jnlpFiles.iterator(); jnlpFileItr.hasNext(); ) 
        {
            
            JnlpFile jnlpFile = (JnlpFile) jnlpFileItr.next();
            
            List jnlpJarResources = jnlpFile.getJarResources();
            
            for ( Iterator jarResourceItr = jnlpJarResources.iterator(); jarResourceItr.hasNext(); )
            {
                JarResource jarResource = (JarResource) jarResourceItr.next();
                
                if ( this.commonJarResources.contains( jarResource ) )
                {
                    String message = "Configuration Error: The jar resource element for artifact "
                                     + jarResource
                                     + " defined in common jar resources is duplicated in the jar "
                                     + "resources configuration of the jnlp file identified by the template file "
                                     + jnlpFile.getTemplateFilename()
                                     + ".";
       
                    throw new MojoExecutionException( message );
                    
                }
                
            }
            
        }
        
    }
    
    /**
     * Checks the configuration of common jar resources. Specifying common jar 
     * resources is optional but if present, each jar resource must have the 
     * same mandatory fields as jar resources configured directly within a 
     * jnlpFile element, but it must not have a configured mainClass element.
     *
     * @throws MojoExecutionException if the config is invalid.
     */
    private void checkCommonJarResources( ) throws MojoExecutionException 
    {
        
        if ( this.commonJarResources == null )
        {
            return;
        }
        
        for (Iterator itr = this.commonJarResources.iterator(); itr.hasNext(); )
        {
            JarResource jarResource = (JarResource) itr.next();
            checkMandatoryJarResourceFields( jarResource );
            
            if ( jarResource.getMainClass() != null )
            {
                throw new MojoExecutionException("Configuration Error: A mainClass must not be specified "
                                                 + "on a JarResource in the commonJarResources collection.");
            }
            
        }
        
    }

    private void checkMandatoryJarResourceFields( JarResource jarResource ) throws MojoExecutionException
    {

        if ( StringUtils.isEmpty( jarResource.getGroupId() )
             || StringUtils.isEmpty( jarResource.getArtifactId() )
             || StringUtils.isEmpty( jarResource.getVersion() ) ) {
            throw new MojoExecutionException(
                    "Configuration error: groupId, artifactId or version missing for jarResource["
                    + jarResource + "]." );
        }

    }
    
    /**
     * Confirms that each jnlpFile element is configured with a unique JNLP file name.
     *
     * @throws MojoExecutionException
     */
    private void checkForUniqueJnlpFilenames() throws MojoExecutionException
    {
        Set filenames = new HashSet( this.jnlpFiles.size() );
        
        for ( Iterator itr = this.jnlpFiles.iterator(); itr.hasNext(); ) 
        {
            JnlpFile jnlpFile = (JnlpFile) itr.next();
            
            if ( !filenames.add( jnlpFile.getOutputFilename() ) ) 
            {
                throw new MojoExecutionException( "Configuration error: Unique JNLP filenames must be provided. "
                                                  + "The following file name appears more than once [" 
                                                  + jnlpFile.getOutputFilename() + "]." );
            }
            
        }
        
    }

    /**
     * Resolve the artifacts represented by the given collection of JarResources and 
     * copy them to the working directory if a newer copy of the file doesn't already 
     * exist there. Transitive dependencies will also be retrieved.
     * 

* Transitive dependencies are added to the list specified as parameter. TODO fix that. * * @throws MojoExecutionException */ private void retrieveJarResources( List jarResources ) throws MojoExecutionException { Set jarResourceArtifacts = new HashSet(); try { //for each configured JarResource, create and resolve the corresponding artifact and //check it for the mainClass if specified for ( Iterator itr = jarResources.iterator(); itr.hasNext(); ) { JarResource jarResource = (JarResource) itr.next(); Artifact artifact = createArtifact(jarResource); getArtifactResolver().resolve( artifact, getRemoteRepositories(), getLocalRepository() ); jarResource.setArtifact( artifact ); checkForMainClass( jarResource ); jarResourceArtifacts.add( artifact ); } if ( !isExcludeTransitive() ) { retrieveTransitiveDependencies( jarResourceArtifacts , jarResources ); } //for each JarResource, copy its artifact to the working directory if necessary for ( Iterator itr = jarResources.iterator(); itr.hasNext(); ) { JarResource jarResource = (JarResource) itr.next(); Artifact artifact = jarResource.getArtifact(); boolean copied = copyJarAsUnprocessedToDirectoryIfNecessary( artifact.getFile(), getWorkDirectory() ); if ( copied ) { String name = artifact.getFile().getName(); if ( getLog().isDebugEnabled() ) { getLog().debug("Adding " + name + " to modifiedJnlpArtifacts list." ); } getModifiedJnlpArtifacts().add( name.substring( 0, name.lastIndexOf( '.' ) ) ); } String hrefValue = buildHrefValue(artifact); jarResource.setHrefValue( hrefValue ); } } catch ( ArtifactResolutionException e ) { throw new MojoExecutionException( "Unable to resolve an artifact", e ); } catch ( ArtifactNotFoundException e ) { throw new MojoExecutionException( "Unable to find an artifact", e ); } catch ( IOException e ) { throw new MojoExecutionException( "Unable to copy an artifact to the working directory", e ); } } private Artifact createArtifact( JarResource jarResource ) { if ( jarResource.getClassifier() == null ) { return getArtifactFactory().createArtifact( jarResource.getGroupId(), jarResource.getArtifactId(), jarResource.getVersion(), Artifact.SCOPE_RUNTIME, "jar" ); } else { return getArtifactFactory().createArtifactWithClassifier( jarResource.getGroupId(), jarResource.getArtifactId(), jarResource.getVersion(), "jar", jarResource.getClassifier() ); } } /** * If the given jarResource is configured with a main class, the underlying artifact * is checked to see if it actually contains the specified class. * * @param jarResource * @throws IllegalStateException if the jarResource's underlying artifact has not yet been resolved. * @throws MojoExecutionException */ private void checkForMainClass( JarResource jarResource ) throws MojoExecutionException { String mainClass = jarResource.getMainClass(); if ( mainClass == null ) { return; } Artifact artifact = jarResource.getArtifact(); if ( artifact == null ) { throw new IllegalStateException( "Implementation Error: The given jarResource cannot be checked for " + "a main class until the underlying artifact has been resolved: [" + jarResource + "]"); } try { if ( !artifactContainsClass( artifact, mainClass ) ) { throw new MojoExecutionException( "The jar specified by the following jarResource does not contain the declared main class:" + jarResource ); } } catch ( MalformedURLException e ) { throw new MojoExecutionException("Attempting to find main class [" + mainClass + "] in [" + artifact + "]", e); } } private void retrieveTransitiveDependencies( Set jarResourceArtifacts , List jarResources ) throws ArtifactResolutionException, ArtifactNotFoundException { // this restricts to runtime and compile scope ScopeArtifactFilter artifactFilter = new ScopeArtifactFilter( Artifact.SCOPE_RUNTIME ); ArtifactResolutionResult result = getArtifactResolver().resolveTransitively( jarResourceArtifacts, getProject().getArtifact(), null, //managedVersions getLocalRepository(), getRemoteRepositories(), this.artifactMetadataSource, artifactFilter ); Set transitiveResolvedArtifacts = result.getArtifacts(); if ( getLog().isDebugEnabled() ) { getLog().debug("transitively resolved artifacts = " + transitiveResolvedArtifacts); getLog().debug("jarResources = " + jarResources); getLog().debug("jarResourceArtifacts = " + jarResourceArtifacts); } //for each transitive dependency, wrap it in a JarResource and add it to the collection of //existing jar resources for ( Iterator itr = transitiveResolvedArtifacts.iterator(); itr.hasNext(); ) { Artifact resolvedArtifact = (Artifact) itr.next(); // this whole double check is ugly as well as this method changing the input variable // we should really improve the way we collect the jarResources if ( !jarResourceArtifacts.contains( resolvedArtifact ) ) { JarResource newJarResource = new JarResource(resolvedArtifact); if ( !jarResources.contains( newJarResource ) ) { newJarResource.setOutputJarVersion( true ); jarResources.add( newJarResource ); } } } } private void generateJnlpFile( JnlpFile jnlpFile ) throws MojoExecutionException { File jnlpOutputFile = new File( getWorkDirectory(), jnlpFile.getOutputFilename() ); Set jarResources = new LinkedHashSet(); jarResources.addAll( jnlpFile.getJarResources() ); if ( this.commonJarResources != null && !this.commonJarResources.isEmpty() ) { for ( Iterator itr = this.commonJarResources.iterator(); itr.hasNext(); ) { JarResource jarResource = (JarResource) itr.next(); jarResources.add( jarResource ); } jarResources.addAll( this.commonJarResources ); } JarResourcesGenerator jnlpGenerator = new JarResourcesGenerator( getProject(), getTemplateDirectory(), "default-jnlp-servlet-template.vm", jnlpOutputFile, jnlpFile.getTemplateFilename(), jarResources, jnlpFile.getMainClass(), getWebstartJarURLForVelocity() ); jnlpGenerator.setExtraConfig( getGeneratorExtraConfig() ); try { jnlpGenerator.generate(); } catch ( Exception e ) { throw new MojoExecutionException( "The following error occurred attempting to generate " + "the JNLP deployment descriptor: " + e, e ); } } private GeneratorExtraConfig getGeneratorExtraConfig() { return new GeneratorExtraConfig() { public String getJnlpSpec() { return "1.0+"; } public String getOfflineAllowed() { return "false"; } public String getAllPermissions() { return "true"; } public String getJ2seVersion() { return "1.5+"; } }; } /** * Generates a version.xml file for all the jarResources configured either in jnlpFile elements * or in the commonJarResources element. * * @throws MojoExecutionException */ private void generateVersionXml() throws MojoExecutionException { Set/*JarResource*/ jarResources = new LinkedHashSet(); //combine the jar resources from commonJarResources and each JnlpFile config for (Iterator itr = this.jnlpFiles.iterator(); itr.hasNext(); ) { JnlpFile jnlpFile = (JnlpFile) itr.next(); jarResources.addAll( jnlpFile.getJarResources() ); } if ( this.commonJarResources != null ) { jarResources.addAll( this.commonJarResources ); } VersionXmlGenerator generator = new VersionXmlGenerator(); generator.generate( getWorkDirectory(), jarResources ); } /** * {@inheritDoc} */ public MavenProject getProject() { return this.project; } /** * Builds the string to be entered in the href attribute of the jar * resource element in the generated JNLP file. This will be equal * to the artifact file name with the version number stripped out. * * @param artifact The underlying artifact of the jar resource. * @return The href string for the given artifact, never null. */ private String buildHrefValue( Artifact artifact ) { StringBuffer sbuf = new StringBuffer(); sbuf.append( artifact.getArtifactId() ); if ( StringUtils.isNotEmpty( artifact.getClassifier() ) ) { sbuf.append( "-" ).append( artifact.getClassifier() ); } sbuf.append( "." ).append( artifact.getArtifactHandler().getExtension() ); return sbuf.toString(); } /** * Copies the contents of the working directory to the output directory. */ private void copyWorkingDirToOutputDir() throws MojoExecutionException { File outputDir = new File(getProject().getBuild().getDirectory(), getProject().getBuild().getFinalName() + File.separator + this.outputDirectoryName); if ( !outputDir.exists() ) { if ( getLog().isInfoEnabled() ) { getLog().info( "Creating JNLP output directory: " + outputDir.getAbsolutePath() ); } if ( !outputDir.mkdirs() ) { throw new MojoExecutionException( "Unable to create the output directory for the jnlp bundle" ); } } try { FileUtils.copyDirectoryStructure(getWorkDirectory(), outputDir); } catch (IOException e) { throw new MojoExecutionException( "An error occurred attempting to copy a file to the JNLP output directory.", e ); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy