View Javadoc
1   package org.codehaus.mojo.webstart.generator;
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 org.apache.commons.lang.BooleanUtils;
23  import org.apache.maven.plugin.logging.Log;
24  import org.apache.maven.project.MavenProject;
25  import org.apache.maven.shared.utils.StringUtils;
26  import org.apache.velocity.Template;
27  import org.apache.velocity.VelocityContext;
28  import org.apache.velocity.app.VelocityEngine;
29  import org.apache.velocity.runtime.log.NullLogSystem;
30  import org.codehaus.plexus.util.WriterFactory;
31  
32  import java.io.Writer;
33  import java.text.DateFormat;
34  import java.text.SimpleDateFormat;
35  import java.util.Date;
36  import java.util.Map;
37  import java.util.Properties;
38  import java.util.TimeZone;
39  
40  /**
41   * The abstract superclass for classes that generate the JNLP files produced by the
42   * various MOJOs available in the plugin.
43   *
44   * @author Kevin Stembridge
45   * @version $Revision$
46   * @since 30 Aug 2007
47   */
48  public abstract class AbstractGenerator<C extends GeneratorExtraConfig>
49  {
50  
51      private VelocityEngine engine;
52  
53      private Template velocityTemplate;
54  
55      private final GeneratorTechnicalConfig config;
56  
57      private final C extraConfig;
58  
59      private Log log;
60  
61      public static final String EOL = System.getProperty( "line.separator" );
62  
63      protected AbstractGenerator( Log log, GeneratorTechnicalConfig config, C extraConfig )
64      {
65  
66          this.log = log;
67          this.config = config;
68          this.extraConfig = extraConfig;
69  
70          Properties props = new Properties();
71  
72          String inputFileTemplatePath = config.getInputFileTemplatePath();
73  
74          if ( inputFileTemplatePath != null )
75          {
76              props.setProperty( VelocityEngine.RUNTIME_LOG_LOGSYSTEM_CLASS,
77                                 "org.apache.velocity.runtime.log.NullLogSystem" );
78              props.setProperty( "file.resource.loader.path", config.getResourceLoaderPath().getAbsolutePath() );
79  
80              initVelocity( props );
81  
82              if ( !engine.templateExists( inputFileTemplatePath ) )
83              {
84                  log.warn( "Warning, template not found. Will probably fail." );
85              }
86          }
87          else
88          {
89              log.info( "No template specified Using default one." );
90  
91              inputFileTemplatePath = config.getDefaultTemplateResourceName();
92  
93              String webstartJarURL = config.getWebstartJarURL();
94              log.debug( "***** Webstart JAR URL: " + webstartJarURL );
95  
96              props = new Properties();
97              props.setProperty( "resource.loader", "jar" );
98              props.setProperty( "jar.resource.loader.description",
99                                 "Jar resource loader for default webstart templates" );
100             props.setProperty( "jar.resource.loader.class",
101                                "org.apache.velocity.runtime.resource.loader.JarResourceLoader" );
102             props.setProperty( "jar.resource.loader.path", webstartJarURL );
103 
104             initVelocity( props );
105 
106             if ( !engine.templateExists( inputFileTemplatePath ) )
107             {
108                 log.error( "Inbuilt template not found!! " + config.getDefaultTemplateResourceName() +
109                                " Will probably fail." );
110             }
111         }
112 
113         try
114         {
115             this.velocityTemplate = engine.getTemplate( inputFileTemplatePath, config.getEncoding() );
116         }
117         catch ( Exception e )
118         {
119             IllegalArgumentException iae =
120                 new IllegalArgumentException( "Could not load the template file from '" + inputFileTemplatePath + "'" );
121             iae.initCause( e );
122             throw iae;
123         }
124     }
125 
126     private void initVelocity( Properties props )
127     {
128         try
129         {
130             engine = new VelocityEngine();
131             engine.setProperty( "runtime.log.logsystem", new NullLogSystem() );
132             engine.init( props );
133         }
134         catch ( Exception e )
135         {
136             IllegalArgumentException iae = new IllegalArgumentException( "Could not initialise Velocity" );
137             iae.initCause( e );
138             throw iae;
139         }
140     }
141 
142     public C getExtraConfig()
143     {
144         return extraConfig;
145     }
146 
147     /**
148      * Generate the JNLP file.
149      *
150      * @throws Exception
151      */
152     public final void generate()
153         throws Exception
154     {
155         VelocityContext context = createAndPopulateContext();
156 
157         Writer writer = WriterFactory.newWriter( config.getOutputFile(), config.getEncoding() );
158 
159         try
160         {
161             velocityTemplate.merge( context, writer );
162             writer.flush();
163         }
164         catch ( Exception e )
165         {
166             throw new Exception(
167                 "Could not generate the template " + velocityTemplate.getName() + ": " + e.getMessage(), e );
168         }
169         finally
170         {
171             writer.close();
172         }
173 
174     }
175 
176     /**
177      * Subclasses must implement this method to return the text that should
178      * replace the $dependencies placeholder in the JNLP template.
179      *
180      * @return The dependencies text, never null.
181      */
182     protected abstract String getDependenciesText();
183 
184     /**
185      * Creates a Velocity context and populates it with replacement values
186      * for our pre-defined placeholders.
187      *
188      * @return Returns a velocity context with system and maven properties added
189      */
190     protected VelocityContext createAndPopulateContext()
191     {
192         VelocityContext context = new VelocityContext();
193 
194         context.put( "dependencies", getDependenciesText() );
195 
196         context.put("arguments", getArgumentsText());
197 
198         // Note: properties that contain dots will not be properly parsed by Velocity. 
199         // Should we replace dots with underscores ?        
200         addPropertiesToContext( System.getProperties(), context );
201 
202         MavenProject mavenProject = config.getMavenProject();
203         String encoding = config.getEncoding();
204 
205         addPropertiesToContext( mavenProject.getProperties(), context );
206         addPropertiesToContext( extraConfig.getProperties(), context );
207 
208         context.put( "project", mavenProject.getModel() );
209         context.put( "jnlpCodebase", extraConfig.getJnlpCodeBase() );
210 
211         // aliases named after the JNLP file structure
212         context.put( "informationTitle", mavenProject.getModel().getName() );
213         context.put( "informationDescription", mavenProject.getModel().getDescription() );
214         if ( mavenProject.getModel().getOrganization() != null )
215         {
216             context.put( "informationVendor", mavenProject.getModel().getOrganization().getName() );
217             context.put( "informationHomepage", mavenProject.getModel().getOrganization().getUrl() );
218         }
219 
220         // explicit timestamps in local and and UTC time zones
221         Date timestamp = new Date();
222         context.put( "explicitTimestamp", dateToExplicitTimestamp( timestamp ) );
223         context.put( "explicitTimestampUTC", dateToExplicitTimestampUTC( timestamp ) );
224 
225         context.put( "outputFile", config.getOutputFile().getName() );
226         context.put( "mainClass", config.getMainClass() );
227 
228         context.put( "encoding", encoding );
229         context.put( "input.encoding", encoding );
230         context.put( "output.encoding", encoding );
231 
232         // TODO make this more extensible
233         context.put( "allPermissions", BooleanUtils.toBoolean( extraConfig.getAllPermissions() ) );
234         context.put( "offlineAllowed", BooleanUtils.toBoolean( extraConfig.getOfflineAllowed() ) );
235         context.put( "jnlpspec", extraConfig.getJnlpSpec() );
236         context.put( "j2seVersion", extraConfig.getJ2seVersion() );
237 
238         return context;
239     }
240 
241     protected abstract String getArgumentsText();
242 
243     private void addPropertiesToContext( Map<?, ?> properties, VelocityContext context )
244     {
245         if ( properties != null )
246         {
247             for ( Object o : properties.keySet() )
248             {
249                 String nextKey = (String) o;
250                 Object nextValue = properties.get( nextKey );
251                 context.put( nextKey, nextValue.toString() );
252             }
253         }
254     }
255 
256     /**
257      * Converts a given date to an explicit timestamp string in local time zone.
258      *
259      * @param date a timestamp to convert.
260      * @return a string representing a timestamp.
261      */
262     private String dateToExplicitTimestamp( Date date )
263     {
264         DateFormat df = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ssZ" );
265         return "TS: " + df.format( date );
266     }
267 
268     /**
269      * Converts a given date to an explicit timestamp string in UTC time zone.
270      *
271      * @param date a timestamp to convert.
272      * @return a string representing a timestamp.
273      */
274     private String dateToExplicitTimestampUTC( Date date )
275     {
276         DateFormat df = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" );
277         df.setTimeZone( TimeZone.getTimeZone( "UTC" ) );
278         return "TS: " + df.format( date ) + "Z";
279     }
280 
281     /**
282      * Add {@code level} space caracteres at the begin of each lines of the
283      * given {@code text}.
284      *
285      * @param level the number of space caracteres to add
286      * @param text  the text to prefix
287      * @return the indented text
288      */
289     protected String indentText( int level, String text )
290     {
291         StringBuilder buffer = new StringBuilder();
292         String[] lines = text.split( "\n" );
293         String prefix = StringUtils.leftPad( "", level );
294         for ( String line : lines )
295         {
296             buffer.append( prefix ).append( line ).append( EOL );
297         }
298         return buffer.toString();
299     }
300 }