View Javadoc

1   package org.apache.maven.plugin.rar;
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.maven.archiver.MavenArchiveConfiguration;
23  import org.apache.maven.archiver.MavenArchiver;
24  import org.apache.maven.artifact.Artifact;
25  import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
26  import org.apache.maven.execution.MavenSession;
27  import org.apache.maven.model.Resource;
28  import org.apache.maven.plugin.AbstractMojo;
29  import org.apache.maven.plugin.MojoExecutionException;
30  import org.apache.maven.plugins.annotations.Component;
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.apache.maven.project.MavenProject;
36  import org.apache.maven.shared.filtering.MavenFilteringException;
37  import org.apache.maven.shared.filtering.MavenResourcesExecution;
38  import org.apache.maven.shared.filtering.MavenResourcesFiltering;
39  import org.codehaus.plexus.archiver.Archiver;
40  import org.codehaus.plexus.archiver.jar.JarArchiver;
41  import org.codehaus.plexus.util.FileUtils;
42  
43  import java.io.File;
44  import java.io.IOException;
45  import java.util.ArrayList;
46  import java.util.Collections;
47  import java.util.Iterator;
48  import java.util.LinkedHashSet;
49  import java.util.List;
50  import java.util.Set;
51  
52  /**
53   * Builds J2EE Resource Adapter Archive (RAR) files.
54   *
55   * @author <a href="stephane.nicoll@gmail.com">Stephane Nicoll</a>
56   * @version $Id: RarMojo.java 1411439 2012-11-19 21:46:22Z olamy $
57   */
58  @Mojo( name = "rar", threadSafe = true, defaultPhase = LifecyclePhase.PACKAGE,
59         requiresDependencyResolution = ResolutionScope.TEST )
60  public class RarMojo
61      extends AbstractMojo
62  {
63      public static final String RA_XML_URI = "META-INF/ra.xml";
64  
65      private static final String[] DEFAULT_INCLUDES = { "**/**" };
66  
67      /**
68       * Single directory for extra files to include in the RAR.
69       */
70      @Parameter( defaultValue = "${basedir}/src/main/rar", required = true )
71      private File rarSourceDirectory;
72  
73      /**
74       * The location of the ra.xml file to be used within the rar file.
75       */
76      @Parameter( defaultValue = "${basedir}/src/main/rar/META-INF/ra.xml" )
77      private File raXmlFile;
78  
79      /**
80       * Specify if the generated jar file of this project should be
81       * included in the rar file ; default is true.
82       */
83      @Parameter
84      private Boolean includeJar = Boolean.TRUE;
85  
86      /**
87       * The location of the manifest file to be used within the rar file.
88       */
89      @Parameter( defaultValue = "${basedir}/src/main/rar/META-INF/MANIFEST.MF" )
90      private File manifestFile;
91  
92      /**
93       * Directory that resources are copied to during the build.
94       */
95      @Parameter( defaultValue = "${project.build.directory}/${project.build.finalName}", required = true )
96      private String workDirectory;
97  
98      /**
99       * The directory for the generated RAR.
100      */
101     @Parameter( defaultValue = "${project.build.directory}", required = true )
102     private String outputDirectory;
103 
104     /**
105      * The name of the RAR file to generate.
106      */
107     @Parameter( alias = "rarName", defaultValue = "${project.build.finalName}", required = true )
108     private String finalName;
109 
110     /**
111      * The maven project.
112      */
113     @Component
114     private MavenProject project;
115 
116     /**
117      * The Jar archiver.
118      */
119     @Component( role = Archiver.class, hint = "jar" )
120     private JarArchiver jarArchiver;
121 
122     /**
123      * The archive configuration to use.
124      * See <a href="http://maven.apache.org/shared/maven-archiver/index.html">Maven Archiver Reference</a>.
125      */
126     @Parameter
127     private MavenArchiveConfiguration archive = new MavenArchiveConfiguration();
128 
129     /**
130      * allow filtering of link{rarSourceDirectory}
131      *
132      * @since 2.3
133      */
134     @Parameter( property = "rar.filterRarSourceDirectory", defaultValue = "false" )
135     private boolean filterRarSourceDirectory;
136 
137 
138     /**
139      * @since 2.3
140      */
141     @Component( role = MavenResourcesFiltering.class, hint = "default" )
142     protected MavenResourcesFiltering mavenResourcesFiltering;
143 
144     /**
145      * @since 2.3
146      */
147     @Parameter( defaultValue = "${session}", required = true, readonly = true )
148     protected MavenSession session;
149 
150     /**
151      * @since 2.3
152      */
153     @Parameter( property = "encoding", defaultValue = "${project.build.sourceEncoding}" )
154     protected String encoding;
155 
156     /**
157      * Whether to escape backslashes and colons in windows-style paths.
158      *
159      * @since 2.3
160      */
161     @Parameter( property = "maven.resources.escapeWindowsPaths", defaultValue = "true" )
162     protected boolean escapeWindowsPaths;
163 
164     /**
165      * Expression preceded with the String won't be interpolated
166      * \${foo} will be replaced with ${foo}
167      *
168      * @since 2.3
169      */
170     @Parameter( property = "maven.resources.escapeString" )
171     protected String escapeString;
172 
173     /**
174      * Overwrite existing files even if the destination files are newer.
175      *
176      * @since 2.3
177      */
178     @Parameter( property = "maven.resources.overwrite", defaultValue = "false" )
179     private boolean overwrite;
180 
181     /**
182      * Copy any empty directories included in the Resources.
183      *
184      * @since 2.3
185      */
186     @Parameter( property = "maven.resources.includeEmptyDirs", defaultValue = "false" )
187     protected boolean includeEmptyDirs;
188 
189     /**
190      * stop searching endToken at the end of line
191      *
192      * @since 2.3
193      */
194     @Parameter( property = "maven.resources.supportMultiLineFiltering", defaultValue = "false" )
195     private boolean supportMultiLineFiltering;
196 
197     /**
198      * @since 2.3
199      */
200     @Parameter( defaultValue = "true" )
201     protected boolean useDefaultDelimiters;
202 
203     /**
204      * <p>
205      * Set of delimiters for expressions to filter within the resources. These delimiters are specified in the
206      * form 'beginToken*endToken'. If no '*' is given, the delimiter is assumed to be the same for start and end.
207      * </p><p>
208      * So, the default filtering delimiters might be specified as:
209      * </p>
210      * <pre>
211      * &lt;delimiters&gt;
212      *   &lt;delimiter&gt;${*}&lt/delimiter&gt;
213      *   &lt;delimiter&gt;@&lt/delimiter&gt;
214      * &lt;/delimiters&gt;
215      * </pre>
216      * <p>
217      * Since the '@' delimiter is the same on both ends, we don't need to specify '@*@' (though we can).
218      * </p>
219      *
220      * @since 2.3
221      */
222     @Parameter
223     protected List<String> delimiters;
224 
225     /**
226      * The list of extra filter properties files to be used along with System properties,
227      * project properties, and filter properties files specified in the POM build/filters section,
228      * which should be used for the filtering during the current mojo execution.
229      * <br/>
230      * Normally, these will be configured from a plugin's execution section, to provide a different
231      * set of filters for a particular execution. For instance, starting in Maven 2.2.0, you have the
232      * option of configuring executions with the id's <code>default-resources</code> and
233      * <code>default-testResources</code> to supply different configurations for the two
234      * different types of resources. By supplying <code>extraFilters</code> configurations, you
235      * can separate which filters are used for which type of resource.
236      *
237      * @since 2.3
238      */
239     @Parameter
240     protected List<String> filters;
241 
242     /**
243      * Additional file extensions to not apply filtering (already defined are : jpg, jpeg, gif, bmp, png)
244      *
245      * @since 2.3
246      */
247     @Parameter
248     protected List<String> nonFilteredFileExtensions;
249 
250     /**
251      * extra resource to include in rar archive
252      *
253      * @since 2.3
254      */
255     @Parameter
256     protected List<RarResource> rarResources;
257 
258 
259     /**
260      * Whether or not warn if the <code>ra.xml</code> file is missing. Set to <code>false</code>
261      * if you want you RAR built without a <code>ra.xml</code> file.
262      * This may be useful if you are building against JCA 1.6 or later.
263      *
264      * @since 2.3
265      */
266     @Parameter( property = "warnOnMissingRaXml", defaultValue = "true" )
267     protected boolean warnOnMissingRaXml = true;
268 
269     private File buildDir;
270 
271 
272     public void execute()
273         throws MojoExecutionException
274     {
275         getLog().debug( " ======= RarMojo settings =======" );
276         getLog().debug( "rarSourceDirectory[" + rarSourceDirectory + "]" );
277         getLog().debug( "manifestFile[" + manifestFile + "]" );
278         getLog().debug( "raXmlFile[" + raXmlFile + "]" );
279         getLog().debug( "workDirectory[" + workDirectory + "]" );
280         getLog().debug( "outputDirectory[" + outputDirectory + "]" );
281         getLog().debug( "finalName[" + finalName + "]" );
282 
283         // Check if jar file is there and if requested, copy it
284         try
285         {
286             if ( includeJar.booleanValue() )
287             {
288                 File generatedJarFile = new File( outputDirectory, finalName + ".jar" );
289                 if ( generatedJarFile.exists() )
290                 {
291                     getLog().info( "Including generated jar file[" + generatedJarFile.getName() + "]" );
292                     FileUtils.copyFileToDirectory( generatedJarFile, getBuildDir() );
293                 }
294             }
295         }
296         catch ( IOException e )
297         {
298             throw new MojoExecutionException( "Error copying generated Jar file", e );
299         }
300 
301         // Copy dependencies
302         try
303         {
304             Set artifacts = project.getArtifacts();
305             for ( Iterator iter = artifacts.iterator(); iter.hasNext(); )
306             {
307                 Artifact artifact = (Artifact) iter.next();
308 
309                 ScopeArtifactFilter filter = new ScopeArtifactFilter( Artifact.SCOPE_RUNTIME );
310                 if ( !artifact.isOptional() && filter.include( artifact )
311                     && artifact.getArtifactHandler().isAddedToClasspath() )
312                 {
313                     getLog().info( "Copying artifact[" + artifact.getGroupId() + ", " + artifact.getId() + ", "
314                                        + artifact.getScope() + "]" );
315                     FileUtils.copyFileToDirectory( artifact.getFile(), getBuildDir() );
316                 }
317             }
318         }
319         catch ( IOException e )
320         {
321             throw new MojoExecutionException( "Error copying RAR dependencies", e );
322         }
323 
324         Resource resource = new Resource();
325         resource.setDirectory( rarSourceDirectory.getAbsolutePath() );
326         resource.setTargetPath( getBuildDir().getAbsolutePath() );
327         resource.setFiltering( filterRarSourceDirectory );
328 
329         List<Resource> resources = new ArrayList<Resource>();
330         resources.add( resource );
331 
332         if ( rarResources != null && !rarResources.isEmpty() )
333         {
334             resources.addAll( rarResources );
335         }
336 
337         MavenResourcesExecution mavenResourcesExecution =
338             new MavenResourcesExecution( resources, getBuildDir(), project, encoding, filters,
339                                          Collections.<String>emptyList(), session );
340 
341         mavenResourcesExecution.setEscapeWindowsPaths( escapeWindowsPaths );
342 
343         // never include project build filters in this call, since we've already accounted for the POM build filters
344         // above, in getCombinedFiltersList().
345         mavenResourcesExecution.setInjectProjectBuildFilters( false );
346 
347         mavenResourcesExecution.setEscapeString( escapeString );
348         mavenResourcesExecution.setOverwrite( overwrite );
349         mavenResourcesExecution.setIncludeEmptyDirs( includeEmptyDirs );
350         mavenResourcesExecution.setSupportMultiLineFiltering( supportMultiLineFiltering );
351 
352         // if these are NOT set, just use the defaults, which are '${*}' and '@'.
353         if ( delimiters != null && !delimiters.isEmpty() )
354         {
355             LinkedHashSet<String> delims = new LinkedHashSet<String>();
356             if ( useDefaultDelimiters )
357             {
358                 delims.addAll( mavenResourcesExecution.getDelimiters() );
359             }
360 
361             for ( String delim : delimiters )
362             {
363                 if ( delim == null )
364                 {
365                     // FIXME: ${filter:*} could also trigger this condition. Need a better long-term solution.
366                     delims.add( "${*}" );
367                 }
368                 else
369                 {
370                     delims.add( delim );
371                 }
372             }
373 
374             mavenResourcesExecution.setDelimiters( delims );
375         }
376 
377         if ( nonFilteredFileExtensions != null )
378         {
379             mavenResourcesExecution.setNonFilteredFileExtensions( nonFilteredFileExtensions );
380         }
381         try
382         {
383             mavenResourcesFiltering.filterResources( mavenResourcesExecution );
384         }
385         catch ( MavenFilteringException e )
386         {
387             throw new MojoExecutionException( "Error copying RAR resources", e );
388         }
389 
390         // Include custom manifest if necessary
391         try
392         {
393             includeCustomRaXmlFile();
394         }
395         catch ( IOException e )
396         {
397             throw new MojoExecutionException( "Error copying ra.xml file", e );
398         }
399 
400         // Check if connector deployment descriptor is there
401         File ddFile = new File( getBuildDir(), RA_XML_URI );
402         if ( !ddFile.exists() && warnOnMissingRaXml )
403         {
404             getLog().warn( "Connector deployment descriptor: " + ddFile.getAbsolutePath() + " does not exist." );
405         }
406 
407         try
408         {
409             File rarFile = new File( outputDirectory, finalName + ".rar" );
410             MavenArchiver archiver = new MavenArchiver();
411             archiver.setArchiver( jarArchiver );
412             archiver.setOutputFile( rarFile );
413 
414             // Include custom manifest if necessary
415             includeCustomManifestFile();
416 
417             archiver.getArchiver().addDirectory( getBuildDir() );
418             archiver.createArchive( session, project, archive );
419 
420             project.getArtifact().setFile( rarFile );
421         }
422         catch ( Exception e )
423         {
424             throw new MojoExecutionException( "Error assembling RAR", e );
425         }
426     }
427 
428     protected File getBuildDir()
429     {
430         if ( buildDir == null )
431         {
432             buildDir = new File( workDirectory );
433         }
434         return buildDir;
435     }
436 
437     private void includeCustomManifestFile()
438         throws IOException
439     {
440         File customManifestFile = manifestFile;
441         if ( !customManifestFile.exists() )
442         {
443             getLog().info( "Could not find manifest file: " + manifestFile + " - Generating one" );
444         }
445         else
446         {
447             getLog().info( "Including custom manifest file[" + customManifestFile + "]" );
448             archive.setManifestFile( customManifestFile );
449             File metaInfDir = new File( getBuildDir(), "META-INF" );
450             FileUtils.copyFileToDirectory( customManifestFile, metaInfDir );
451         }
452     }
453 
454     private void includeCustomRaXmlFile()
455         throws IOException
456     {
457         if ( raXmlFile == null )
458         {
459             return;
460         }
461         File raXml = raXmlFile;
462         if ( raXml.exists() )
463         {
464             getLog().info( "Using ra.xml " + raXmlFile );
465             File metaInfDir = new File( getBuildDir(), "META-INF" );
466             FileUtils.copyFileToDirectory( raXml, metaInfDir );
467         }
468     }
469 }