package org.codehaus.mojo.webstart.generator;
/*
* 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.Writer;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Properties;
import java.util.TimeZone;
import org.apache.maven.project.MavenProject;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.log.NullLogSystem;
import org.codehaus.plexus.util.WriterFactory;
/**
* The abstract superclass for classes that generate the JNLP files produced by the
* various MOJOs available in the plugin.
*
* @author Kevin Stembridge
* @since 30 Aug 2007
* @version $Revision$
*
*/
public abstract class AbstractGenerator
{
private VelocityEngine engine;
private final MavenProject mavenProject;
private Template velocityTemplate;
private final File outputFile;
private final String mainClass;
private GeneratorExtraConfig extraConfig;
/**
* Creates a new {@code AbstractGenerator}.
*
* @param resourceLoaderPath used to find the template in conjunction to inputFileTemplatePath
* @param outputFile The location of the file to be generated.
* @param inputFileTemplatePath relative to resourceLoaderPath
* @param mainClass The text that should replace the $mainClass placeholder in the JNLP template.
*
* @throws IllegalArgumentException if any argument is null.
*/
protected AbstractGenerator( MavenProject mavenProject,
File resourceLoaderPath,
String defaultTemplateResourceName,
File outputFile,
String inputFileTemplatePath,
String mainClass,
String webstartJarURL )
{
if ( mavenProject == null )
{
throw new IllegalArgumentException( "mavenProject must not be null" );
}
if ( resourceLoaderPath == null )
{
throw new IllegalArgumentException( "resourceLoaderPath must not be null" );
}
if ( outputFile == null )
{
throw new IllegalArgumentException( "outputFile must not be null" );
}
if ( mainClass == null )
{
throw new IllegalArgumentException( "mainClass must not be null" );
}
this.outputFile = outputFile;
this.mainClass = mainClass;
this.mavenProject = mavenProject;
Properties props = new Properties();
if ( inputFileTemplatePath != null )
{
props.setProperty( VelocityEngine.RUNTIME_LOG_LOGSYSTEM_CLASS,
"org.apache.velocity.runtime.log.NullLogSystem" );
props.setProperty( "file.resource.loader.path", resourceLoaderPath.getAbsolutePath() );
initVelocity( props );
if ( !engine.templateExists( inputFileTemplatePath ) )
{
System.out.println( "Warning, template not found. Will probably fail." );
}
}
else
{
System.out.println( "No template specified Using default one." );
inputFileTemplatePath = defaultTemplateResourceName;
System.out.println( "***** Webstart JAR URL: " + webstartJarURL );
props = new Properties();
props.setProperty( "resource.loader", "jar" );
props.setProperty( "jar.resource.loader.description",
"Jar resource loader for default webstart templates" );
props.setProperty( "jar.resource.loader.class",
"org.apache.velocity.runtime.resource.loader.JarResourceLoader" );
props.setProperty( "jar.resource.loader.path", webstartJarURL );
initVelocity( props );
if ( ! engine.templateExists( inputFileTemplatePath ) )
{
System.out.println( "Inbuilt template not found!! " + defaultTemplateResourceName
+ " Will probably fail." );
}
}
try
{
this.velocityTemplate = engine.getTemplate( inputFileTemplatePath );
}
catch ( Exception e )
{
IllegalArgumentException iae =
new IllegalArgumentException( "Could not load the template file from '" + inputFileTemplatePath + "'" );
iae.initCause( e );
throw iae;
}
}
private void initVelocity( Properties props )
{
try
{
engine = new VelocityEngine();
engine.setProperty( "runtime.log.logsystem", new NullLogSystem() );
engine.init( props );
}
catch ( Exception e )
{
IllegalArgumentException iae = new IllegalArgumentException( "Could not initialise Velocity" );
iae.initCause( e );
throw iae;
}
}
public void setExtraConfig( GeneratorExtraConfig extraConfig )
{
this.extraConfig = extraConfig;
}
/**
* Generate the JNLP file.
* @throws Exception
*/
public final void generate() throws Exception
{
VelocityContext context = createAndPopulateContext();
Writer writer = WriterFactory.newXmlWriter( outputFile );
try
{
velocityTemplate.merge( context, writer );
writer.flush();
}
catch ( Exception e )
{
throw new Exception( "Could not generate the template " + velocityTemplate.getName()
+ ": " + e.getMessage(), e );
}
finally
{
writer.close();
}
}
/**
* Subclasses must implement this method to return the text that should
* replace the $dependencies placeholder in the JNLP template.
* @return The dependencies text, never null.
*/
protected abstract String getDependenciesText( );
/**
* Creates a Velocity context and populates it with replacement values
* for our pre-defined placeholders.
*
* @return Returns a velocity context with system and maven properties added
*/
private VelocityContext createAndPopulateContext()
{
VelocityContext context = new VelocityContext();
context.put( "dependencies", getDependenciesText( ) );
// Note: properties that contain dots will not be properly parsed by Velocity.
// Should we replace dots with underscores ?
addPropertiesToContext( System.getProperties(), context );
addPropertiesToContext( mavenProject.getProperties(), context );
context.put( "project", mavenProject.getModel() );
// aliases named after the JNLP file structure
context.put( "informationTitle", mavenProject.getModel().getName() );
context.put( "informationDescription", mavenProject.getModel().getDescription() );
if ( mavenProject.getModel().getOrganization() != null )
{
context.put( "informationVendor", mavenProject.getModel().getOrganization().getName() );
context.put( "informationHomepage", mavenProject.getModel().getOrganization().getUrl() );
}
// explicit timestamps in local and and UTC time zones
Date timestamp = new Date();
context.put( "explicitTimestamp", dateToExplicitTimestamp( timestamp ) );
context.put( "explicitTimestampUTC", dateToExplicitTimestampUTC( timestamp ) );
context.put( "outputFile", outputFile.getName() );
context.put( "mainClass", this.mainClass );
// TODO make this more extensible
context.put( "allPermissions", extraConfig.getJnlpSpec() );
context.put( "offlineAllowed", extraConfig.getOfflineAllowed() );
context.put( "jnlpspec", extraConfig.getJnlpSpec() );
context.put( "j2seVersion", extraConfig.getJ2seVersion() );
return context;
}
private void addPropertiesToContext( Properties properties, VelocityContext context )
{
for ( Iterator iter = properties.keySet().iterator(); iter.hasNext(); )
{
String nextKey = ( String ) iter.next();
String nextValue = properties.getProperty( nextKey );
context.put( nextKey, nextValue );
}
}
/**
* Converts a given date to an explicit timestamp string in local time zone.
*
* @param date a timestamp to convert.
* @return a string representing a timestamp.
*/
private String dateToExplicitTimestamp( Date date )
{
DateFormat df = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ssZ" );
return new StringBuffer( "TS: " ).append( df.format( date ) ).toString();
}
/**
* Converts a given date to an explicit timestamp string in UTC time zone.
*
* @param date a timestamp to convert.
* @return a string representing a timestamp.
*/
private String dateToExplicitTimestampUTC( Date date )
{
DateFormat df = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" );
df.setTimeZone( TimeZone.getTimeZone( "UTC" ) );
return new StringBuffer( "TS: " ).append( df.format( date ) ).append( "Z" ).toString();
}
}