View Javadoc

1   package org.alfresco.maven.plugin.amp;
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.alfresco.maven.plugin.amp.overlay.OverlayManager;
23  import org.alfresco.maven.plugin.amp.packaging.AmpPackagingContext;
24  import org.alfresco.maven.plugin.amp.packaging.AmpPackagingTask;
25  import org.alfresco.maven.plugin.amp.packaging.AmpPostPackagingTask;
26  import org.alfresco.maven.plugin.amp.packaging.AmpProjectPackagingTask;
27  import org.alfresco.maven.plugin.amp.packaging.OverlayPackagingTask;
28  import org.alfresco.maven.plugin.amp.packaging.SaveAmpStructurePostPackagingTask;
29  import org.alfresco.maven.plugin.amp.util.AmpStructure;
30  import org.alfresco.maven.plugin.amp.util.AmpStructureSerializer;
31  import org.alfresco.maven.plugin.amp.util.CompositeMap;
32  import org.alfresco.maven.plugin.amp.util.PropertyUtils;
33  import org.alfresco.maven.plugin.amp.util.ReflectionProperties;
34  
35  
36  import org.apache.maven.archiver.MavenArchiveConfiguration;
37  import org.apache.maven.model.Resource;
38  import org.apache.maven.plugin.AbstractMojo;
39  import org.apache.maven.plugin.MojoExecutionException;
40  import org.apache.maven.plugin.MojoFailureException;
41  import org.apache.maven.plugin.logging.Log;
42  import org.apache.maven.project.MavenProject;
43  import org.codehaus.plexus.archiver.jar.JarArchiver;
44  import org.codehaus.plexus.archiver.manager.ArchiverManager;
45  import org.codehaus.plexus.util.StringUtils;
46  
47  import java.io.File;
48  import java.io.IOException;
49  import java.util.ArrayList;
50  import java.util.Arrays;
51  import java.util.Iterator;
52  import java.util.List;
53  import java.util.Map;
54  import java.util.Properties;
55  
56  public abstract class AbstractAmpMojo extends AbstractMojo
57  {
58  
59      /***
60       * Returns a string array of the classes and resources to be excluded from the jar excludes  to be used
61       * when assembling/copying the AMP.
62       *
63       * @return an array of tokens to exclude
64       */
65      protected String[] getExcludes()
66      {
67          List excludeList = new ArrayList();
68          if ( StringUtils.isNotEmpty( mAmpJarExcludes ) )
69          {
70              excludeList.addAll( Arrays.asList( StringUtils.split( mAmpJarExcludes, "," ) ) );
71          }
72  
73          return (String[]) excludeList.toArray( EMPTY_STRING_ARRAY );
74      }
75  
76      /***
77       * Returns a string array  of the classes and resources to be included from the jar assembling/copying the war.
78       *
79       * @return an array of tokens to include
80       */
81      protected String[] getIncludes()
82      {
83          return StringUtils.split( StringUtils.defaultString( mAmpJarIncludes ), "," );
84      }
85      
86      /***
87     	 * Returns a string array  of the resources to be included in the AMP web/ folder.
88       *
89       * @return an array of tokens to include
90       */
91      protected String[] getWebIncludes()
92      {
93          return StringUtils.split( StringUtils.defaultString( mAmpWebIncludes ), "," );
94      }
95      
96      /***
97     	 * Returns a string array  of the resources to be excluded in the AMP web/ folder.
98     	 *  
99       * @return an array of tokens to exclude
100      */
101     protected String[] getWebExcludes()
102     {
103     	List excludeList = new ArrayList();
104         if ( StringUtils.isNotEmpty( mAmpWebExcludes ) )
105         {
106             excludeList.addAll( Arrays.asList( StringUtils.split( mAmpWebExcludes, "," ) ) );
107         }
108 
109         return (String[]) excludeList.toArray( EMPTY_STRING_ARRAY );
110 
111     }
112     
113 
114     /***
115      * Returns a string array of the excludes to be used
116      * when adding dependent AMPs as an overlay onto this AMP.
117      *
118      * @return an array of tokens to exclude
119      */
120     protected String[] getDependentAmpExcludes()
121     {
122         String[] excludes;
123         if ( StringUtils.isNotEmpty( dependentAmpExcludes ) )
124         {
125             excludes = StringUtils.split( dependentAmpExcludes, "," );
126         }
127         else
128         {
129             excludes = EMPTY_STRING_ARRAY;
130         }
131         return excludes;
132     }
133 
134     /***
135      * Returns a string array of the includes to be used
136      * when adding dependent AMP as an overlay onto this AMP.
137      *
138      * @return an array of tokens to include
139      */
140     protected String[] getDependentAmpIncludes()
141     {
142         return StringUtils.split( StringUtils.defaultString( dependentAmpIncludes ), "," );
143     }
144 
145     public void buildExplodedAmp( File webappDirectory )
146         throws MojoExecutionException, MojoFailureException
147     {
148         webappDirectory.mkdirs();
149 
150         try
151         {
152             buildAmp( mProject, webappDirectory );
153         }
154         catch ( IOException e )
155         {
156             throw new MojoExecutionException( "Could not build AMP", e );
157         }
158     }
159 
160 
161     /***
162      * Builds the webapp for the specified project with the new packaging task
163      * thingy
164      * <p/>
165      * Classes, libraries and tld files are copied to
166      * the <tt>webappDirectory</tt> during this phase.
167      *
168      * @param project         the maven project
169      * @param webappDirectory the target directory
170      * @throws MojoExecutionException if an error occured while packaging the webapp
171      * @throws MojoFailureException   if an unexpected error occured while packaging the webapp
172      * @throws IOException            if an error occured while copying the files
173      */
174     public void buildAmp( MavenProject project, File webappDirectory )
175         throws MojoExecutionException, MojoFailureException, IOException
176     {
177 
178         AmpStructure cache;
179         if ( mUseCache && mCacheFile.exists() )
180         {
181             cache = new AmpStructure( webappStructureSerialier.fromXml( mCacheFile ) );
182         }
183         else
184         {
185             cache = new AmpStructure( null );
186         }
187 
188         final long startTime = System.currentTimeMillis();
189         getLog().info( "Assembling AMP [" + project.getArtifactId() + "] in [" + webappDirectory + "]" );
190 
191         final OverlayManager overlayManager =
192             new OverlayManager( mOverlays, project, dependentAmpIncludes, dependentAmpExcludes );
193         final List packagingTasks = getPackagingTasks( overlayManager );
194         final AmpPackagingContext context = new DefaultAmpPackagingContext( webappDirectory, cache, overlayManager );
195         final Iterator it = packagingTasks.iterator();
196         while ( it.hasNext() )
197         {
198             AmpPackagingTask ampPackagingTask = (AmpPackagingTask) it.next();
199             ampPackagingTask.performPackaging( context );
200         }
201 
202         // Post packaging
203         final List postPackagingTasks = getPostPackagingTasks();
204         final Iterator it2 = postPackagingTasks.iterator();
205         while ( it2.hasNext() )
206         {
207             AmpPostPackagingTask task = (AmpPostPackagingTask) it2.next();
208             task.performPostPackaging( context );
209 
210         }
211         getLog().info( "AMP assembled in[" + ( System.currentTimeMillis() - startTime ) + " msecs]" );
212 
213     }
214 
215     /***
216      * Returns a <tt>List</tt> of the {@link org.alfresco.maven.plugin.amp.packaging.AmpPackagingTask}
217      * instances to invoke to perform the packaging.
218      *
219      * @param overlayManager the overlay manager
220      * @return the list of packaging tasks
221      * @throws MojoExecutionException if the packaging tasks could not be built
222      */
223     private List getPackagingTasks( OverlayManager overlayManager )
224         throws MojoExecutionException
225     {
226         final List packagingTasks = new ArrayList();
227         final List resolvedOverlays = overlayManager.getOverlays();
228         final Iterator it = resolvedOverlays.iterator();
229         while ( it.hasNext() )
230         {
231             Overlay overlay = (Overlay) it.next();
232             if ( overlay.isCurrentProject() )
233             {
234                 packagingTasks.add( new AmpProjectPackagingTask( mAmpResources, mModuleProperties) );
235             }
236             else
237             {
238                 packagingTasks.add( new OverlayPackagingTask( overlay ) );
239             }
240         }
241         return packagingTasks;
242     }
243 
244 
245     /***
246      * Returns a <tt>List</tt> of the {@link org.alfresco.maven.plugin.amp.packaging.AmpPostPackagingTask}
247      * instances to invoke to perform the post-packaging.
248      *
249      * @return the list of post packaging tasks
250      */
251     private List getPostPackagingTasks()
252     {
253         final List postPackagingTasks = new ArrayList();
254         if ( mUseCache )
255         {
256             postPackagingTasks.add( new SaveAmpStructurePostPackagingTask( mCacheFile ) );
257         }
258         // TODO add lib scanning to detect duplicates
259         return postPackagingTasks;
260     }
261 
262     
263     /***
264      * The maven project.
265      *
266      * @parameter expression="${project}"
267      * @required
268      * @readonly
269      */
270     private MavenProject mProject;
271 
272     /***
273      * The directory containing generated classes.
274      *
275      * @parameter expression="${project.build.outputDirectory}"
276      * @required
277      * @readonly
278      */
279     private File mClassesDirectory;
280     
281 
282     /***
283      * The Jar archiver needed for archiving classes directory into jar file under WEB-INF/lib.
284      *
285      * @parameter expression="${component.org.codehaus.plexus.archiver.Archiver#jar}"
286      * @required
287      */
288     private JarArchiver mJarArchiver;
289     
290 
291     /***
292      * The directory where the webapp is built.
293      *
294      * @parameter expression="${project.build.directory}/${project.build.finalName}"
295      * @required
296      */
297     private File mAmpDirectory;
298 
299     /***
300      * Single directory for extra files to include in the AMP.
301      *
302      * @parameter expression="${project.build.outputDirectory}"
303      * @required
304      */
305     private File mAmpConfigDirectory;
306 
307     /***
308      * Single directory for extra files to include in the AMP.
309      *
310      * @parameter expression="${basedir}/src/main/webapp"
311      * @required
312      */
313     private File mAmpWebDirectory;
314 
315     /***
316      * The list of webResources we want to transfer.
317      *
318      * @parameter
319      */
320     private Resource[] mAmpResources;
321 
322     /***
323      * Filters (property files) to include during the interpolation of the pom.xml.
324      *
325      * @parameter expression="${project.build.filters}"
326      */
327     private List filters;
328 
329     /***
330      * The path to the web.xml file to use.
331      *
332      * @parameter expression="${maven.amp.moduleProperties}" default-value="${project.basedir}/module.properties"
333      */
334     private File mModuleProperties;
335 
336 
337     /***
338      * Directory to unpack dependent AMPs into if needed
339      *
340      * @parameter expression="${project.build.directory}/amp/work"
341      * @required
342      */
343     private File mWorkDirectory;
344 
345     /***
346      * The file name mapping to use to copy libraries and tlds. If no file mapping is
347      * set (default) the file is copied with its standard name.
348      *
349      * @parameter
350      * @since 2.0.3
351      */
352     private String mOutputFileNameMapping;
353 
354     /***
355      * The file containing the webapp structure cache.
356      *
357      * @parameter expression="${project.build.directory}/amp/work/amp-cache.xml"
358      * @required
359      * @since 2.1
360      */
361     private File mCacheFile;
362 
363     /***
364      * Whether the cache should be used to save the status of the webapp
365      * accross multiple runs.
366      *
367      * @parameter expression="${useCache}" default-value="true"
368      * @since 2.1
369      */
370     private boolean mUseCache = true;
371 
372     
373     
374     /***
375      * To look up Archiver/UnArchiver implementations
376      *
377      * @parameter expression="${component.org.codehaus.plexus.archiver.manager.ArchiverManager}"
378      * @required
379      */
380     protected ArchiverManager mArchiverManager;
381 
382     private static final String META_INF = "META-INF";
383 
384     public static final String DEFAULT_FILE_NAME_MAPPING_CLASSIFIER =
385         "${artifactId}-${version}-${classifier}.${extension}";
386 
387     public static final String DEFAULT_FILE_NAME_MAPPING = "${artifactId}-${version}.${extension}";
388 
389     /***
390      * The comma separated list of tokens to include in the AMP internal JAR. Default **.
391      * Default is '**'.
392      *
393      * @parameter alias="includes"
394      */
395     private String mAmpJarIncludes = "**";
396 
397     /***
398      * The comma separated list of tokens to exclude from the AMP created JAR file. By default module configuration is left outside jars.
399      *
400      * @parameter alias="excludes" default-value="alfresco/module/**"
401      */
402     private String mAmpJarExcludes;
403     
404     
405     /***
406      * The comma separated list of tokens to include in the AMP internal JAR. Default **.
407      * Default is '**'.
408      *
409      * @parameter alias="webIncludes" default-value="**"
410      */
411     private String mAmpWebIncludes;
412 
413     /***
414      * The comma separated list of tokens to exclude from the AMP created JAR file. By default module configuration is left outside jars.
415      *
416      * @parameter alias="webExcludes"
417      */
418     private String mAmpWebExcludes;
419 
420     /***
421      * The comma separated list of tokens to include when doing
422      * a AMP overlay.
423      * Default is '**'
424      *
425      * @parameter
426      */
427     private String dependentAmpIncludes = "**/**";
428 
429     /***
430      * The comma separated list of tokens to exclude when doing
431      * a AMP overlay.
432      *
433      * @parameter
434      */
435     private String dependentAmpExcludes = "META-INF/**";
436 
437     /***
438      * The overlays to apply.
439      *
440      * @parameter
441      * @since 2.1
442      */
443     private List mOverlays = new ArrayList();
444 
445     /***
446      * The maven archive configuration to use.
447      *
448      * @parameter
449      */
450     protected MavenArchiveConfiguration archive = new MavenArchiveConfiguration();
451 
452     private static final String[] EMPTY_STRING_ARRAY = {};
453 
454     private final AmpStructureSerializer webappStructureSerialier = new AmpStructureSerializer();
455   
456     
457     
458         
459     // AMP packaging implementation
460 
461     private class DefaultAmpPackagingContext
462         implements AmpPackagingContext
463     {
464 
465 
466         private final AmpStructure webappStructure;
467 
468         private final File mAmpDirectory;
469 
470         private final OverlayManager overlayManager;
471 
472         public DefaultAmpPackagingContext( File webappDirectory, final AmpStructure webappStructure,
473                                            final OverlayManager overlayManager )
474         {
475             this.mAmpDirectory = webappDirectory;
476             this.webappStructure = webappStructure;
477             this.overlayManager = overlayManager;
478 
479             // This is kinda stupid but if we loop over the current overlays and we request the path structure
480             // it will register it. This will avoid wrong warning messages in a later phase
481             final Iterator it = overlayManager.getOverlayIds().iterator();
482             while ( it.hasNext() )
483             {
484                 String overlayId = (String) it.next();
485                 webappStructure.getStructure( overlayId );
486             }
487         }
488 
489         public MavenProject getProject()
490         {
491             return mProject;
492         }
493 
494         public File getAmpDirectory()
495         {
496             return mAmpDirectory;
497         }
498 
499         public File getClassesDirectory()
500         {
501             return mClassesDirectory;
502         }
503 
504         public Log getLog()
505         {
506             return AbstractAmpMojo.this.getLog();
507         }
508 
509         public String getOutputFileNameMapping()
510         {
511             return mOutputFileNameMapping;
512         }
513 
514         public File getAmpWebDirectory()
515         {
516             return mAmpWebDirectory;
517         }
518 
519         public String[] getAmpJarIncludes()
520         {
521             return getIncludes();
522         }
523 
524         public String[] getAmpJarExcludes()
525         {
526             return getExcludes();
527         }
528 
529         public File getOverlaysWorkDirectory()
530         {
531             return mWorkDirectory;
532         }
533 
534         public ArchiverManager getArchiverManager()
535         {
536             return mArchiverManager;
537         }
538 
539         public MavenArchiveConfiguration getArchive()
540         {
541             return archive;
542         }
543 
544         public JarArchiver getJarArchiver()
545         {
546             return mJarArchiver;
547         }
548 
549         public List getFilters()
550         {
551             return filters;
552         }
553 
554         public Map getFilterProperties()
555             throws MojoExecutionException
556         {
557             Map filterProperties = new Properties();
558 
559             // System properties
560             filterProperties.putAll( System.getProperties() );
561 
562             // Project properties
563             filterProperties.putAll( mProject.getProperties() );
564 
565             for ( Iterator i = filters.iterator(); i.hasNext(); )
566             {
567                 String filtersfile = (String) i.next();
568 
569                 try
570                 {
571                     Properties properties = PropertyUtils.loadPropertyFile( new File( filtersfile ), true, true );
572 
573                     filterProperties.putAll( properties );
574                 }
575                 catch ( IOException e )
576                 {
577                     throw new MojoExecutionException( "Error loading property file '" + filtersfile + "'", e );
578                 }
579             }
580 
581             // can't putAll, as ReflectionProperties doesn't enumerate - so we make a composite map with the project variables as dominant
582             return new CompositeMap( new ReflectionProperties( mProject ), filterProperties );
583         }
584 
585         public AmpStructure getAmpStructure()
586         {
587             return webappStructure;
588         }
589 
590         public List getOwnerIds()
591         {
592             return overlayManager.getOverlayIds();
593         }
594 
595         /***
596          * @see org.alfresco.maven.plugin.amp.packaging.AmpPackagingContext#getAmpConfigDirectory()
597          */
598         public File getAmpConfigDirectory()
599         {
600             return mAmpConfigDirectory;
601         }
602 
603 		public String[] getAmpWebExcludes() {
604 			return getWebExcludes();
605 		}
606 
607 		public String[] getAmpWebIncludes() {
608 			return getWebIncludes();
609 		}
610 
611     }
612 
613     public MavenProject getProject()
614     {
615         return mProject;
616     }
617 
618     public void setProject( MavenProject project )
619     {
620         this.mProject = project;
621     }
622 
623     public File getClassesDirectory()
624     {
625         return mClassesDirectory;
626     }
627 
628     public void setClassesDirectory( File classesDirectory )
629     {
630         this.mClassesDirectory = classesDirectory;
631     }
632 
633     public File getAmpDirectory()
634     {
635         return mAmpDirectory;
636     }
637 
638     public void setAmpDirectory( File webappDirectory )
639     {
640         this.mAmpDirectory = webappDirectory;
641     }
642 
643     public File getAmpSourceDirectory()
644     {
645         return mAmpWebDirectory;
646     }
647 
648     public void setAmpSourceDirectory( File ampSourceDirectory )
649     {
650         this.mAmpWebDirectory = ampSourceDirectory;
651     }
652 
653     public File getWebXml()
654     {
655         return mModuleProperties;
656     }
657 
658     public void setWebXml( File webXml )
659     {
660         this.mModuleProperties = webXml;
661     }
662 
663    
664     public String getOutputFileNameMapping()
665     {
666         return mOutputFileNameMapping;
667     }
668 
669     public void setOutputFileNameMapping( String outputFileNameMapping )
670     {
671         this.mOutputFileNameMapping = outputFileNameMapping;
672     }
673 
674     public List getOverlays()
675     {
676         return mOverlays;
677     }
678 
679     public void setOverlays( List overlays )
680     {
681         this.mOverlays = overlays;
682     }
683 
684     public void addOverlay( Overlay overlay )
685     {
686         mOverlays.add( overlay );
687     }
688 
689  
690     public JarArchiver getJarArchiver()
691     {
692         return mJarArchiver;
693     }
694 
695     public void setJarArchiver( JarArchiver jarArchiver )
696     {
697         this.mJarArchiver = jarArchiver;
698     }
699 
700     public Resource[] getAmpResources()
701     {
702         return mAmpResources;
703     }
704 
705     public void setAmpResources( Resource[] webResources )
706     {
707         this.mAmpResources = webResources;
708     }
709 
710     public List getFilters()
711     {
712         return filters;
713     }
714 
715     public void setFilters( List filters )
716     {
717         this.filters = filters;
718     }
719 
720     public File getWorkDirectory()
721     {
722         return mWorkDirectory;
723     }
724 
725     public void setWorkDirectory( File workDirectory )
726     {
727         this.mWorkDirectory = workDirectory;
728     }
729 
730     public File getCacheFile()
731     {
732         return mCacheFile;
733     }
734 
735     public void setCacheFile( File cacheFile )
736     {
737         this.mCacheFile = cacheFile;
738     }
739 
740     public void setAmpSourceIncludes( String ampSourceIncludes )
741     {
742         this.mAmpJarIncludes = ampSourceIncludes;
743     }
744 
745     public String getAmpJarExcludes()
746     {
747         return mAmpJarExcludes;
748     }
749 
750     public void setAmpJarExcludes( String ampJarExcludes )
751     {
752         this.mAmpJarExcludes = ampJarExcludes;
753     }
754 
755 
756     public boolean isUseCache()
757     {
758         return mUseCache;
759     }
760 
761     public void setUseCache( boolean useCache )
762     {
763         this.mUseCache = useCache;
764     }
765 }