2011/08/05 - Jakarta Cactus has been retired.

For more information, please explore the Attic.

View Javadoc

1   /* 
2    * ========================================================================
3    * 
4    * Licensed to the Apache Software Foundation (ASF) under one or more
5    * contributor license agreements.  See the NOTICE file distributed with
6    * this work for additional information regarding copyright ownership.
7    * The ASF licenses this file to You under the Apache License, Version 2.0
8    * (the "License"); you may not use this file except in compliance with
9    * the License.  You may obtain a copy of the License at
10   * 
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   * 
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   * 
19   * ========================================================================
20   */
21  package org.apache.cactus.maven2.mojos;
22  
23  import java.io.File;
24  import java.io.FileInputStream;
25  import java.io.IOException;
26  import java.text.DecimalFormat;
27  import java.util.ArrayList;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Random;
31  
32  import org.apache.cactus.integration.api.cactify.CactifyUtils;
33  import org.apache.cactus.integration.api.version.Version;
34  import org.apache.maven.archiver.MavenArchiveConfiguration;
35  import org.apache.maven.archiver.MavenArchiver;
36  import org.apache.maven.artifact.DependencyResolutionRequiredException;
37  import org.apache.maven.artifact.factory.ArtifactFactory;
38  import org.apache.maven.artifact.installer.ArtifactInstaller;
39  import org.apache.maven.artifact.repository.ArtifactRepository;
40  import org.apache.maven.plugin.AbstractMojo;
41  import org.apache.maven.plugin.MojoExecutionException;
42  import org.apache.maven.plugin.MojoFailureException;
43  import org.apache.maven.plugin.assembly.archive.ArchiveExpansionException;
44  import org.apache.maven.plugin.assembly.utils.AssemblyFileUtils;
45  import org.apache.maven.project.MavenProject;
46  import org.apache.maven.shared.model.fileset.FileSet;
47  import org.apache.tools.ant.types.XMLCatalog;
48  import org.codehaus.cargo.container.internal.util.ResourceUtils;
49  import org.codehaus.cargo.maven2.log.MavenLogger;
50  import org.codehaus.cargo.module.webapp.DefaultWarArchive;
51  import org.codehaus.cargo.module.webapp.EjbRef;
52  import org.codehaus.cargo.module.webapp.WarArchive;
53  import org.codehaus.cargo.module.webapp.WebXml;
54  import org.codehaus.cargo.module.webapp.WebXmlIo;
55  import org.codehaus.cargo.module.webapp.WebXmlUtils;
56  import org.codehaus.cargo.module.webapp.WebXmlVersion;
57  import org.codehaus.cargo.module.webapp.merge.WebXmlMerger;
58  import org.codehaus.cargo.util.log.Logger;
59  import org.codehaus.plexus.archiver.ArchiverException;
60  import org.codehaus.plexus.archiver.jar.ManifestException;
61  import org.codehaus.plexus.archiver.manager.ArchiverManager;
62  import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
63  import org.codehaus.plexus.archiver.war.WarArchiver;
64  import org.codehaus.plexus.util.FileUtils;
65  import org.jdom.JDOMException;
66  
67  /**
68   * A maven2 mojo that injects elements necessary to run Cactus tests into an
69   * existing WAR file.
70   * 
71   * @version $Id: CactifyWarMojo.java 394252 2008-04-29 04:20:17Z ptahchiev $
72   * @goal cactifywar
73   * @requiresDependencyResolution compile
74   */
75  public class CactifyWarMojo extends AbstractMojo
76  {
77      /**
78       * Name of the generated web app file.
79       */
80      private String FILE_NAME = "cactus.war";
81      
82      /**
83       * Context of the cactus web application.
84       * @parameter
85       */
86      private String context;
87  	
88      /**
89       * Get some non-crypto-grade randomness from various places.
90       */
91      private static Random rand = new Random(System.currentTimeMillis()
92              + Runtime.getRuntime().freeMemory());
93      
94      /**
95       * Used to create artifacts.
96       *
97       * @component
98       */
99      private ArtifactFactory artifactFactory;
100 
101     /**
102      * For resolving entities such as DTDs.
103      */
104     private XMLCatalog xmlCatalog = null;
105 
106     /**
107      * The archive that contains the web-app that should be cactified.
108      * @parameter
109      */
110     private File srcFile;
111     
112     /**
113      * Test classes.
114      * @parameter
115      */
116     private FileSet testClasses;
117     
118     /**
119      * The War archiver.
120      *
121      * @parameter expression="${component.org.codehaus.plexus.archiver.Archiver#war}"
122      * @required
123      */
124     private WarArchiver warArchiver;
125     
126     /**
127      * The maven project.
128      *
129      * @parameter expression="${project}"
130      * @required
131      * @readonly
132      */
133     private MavenProject project;
134     
135     /**
136      * The maven archive configuration to use.
137      *
138      * @parameter
139      */
140     private MavenArchiveConfiguration archive = new MavenArchiveConfiguration();
141 
142     /**
143      * Location of the descriptor of which the content should be merged into 
144      * the descriptor of the cactified archive.
145      * @parameter
146      */
147     private File mergeWebXml;
148 
149     /**
150      * The Cactus test redirectors.
151      * @parameter
152      */
153     private final List redirectors = new ArrayList();
154     
155     /**
156      * List of ejb-refs to add to the deployment descriptor 
157      * of the cactified war.
158      */
159     private final List ejbRefs = new ArrayList();
160     
161     /**
162      * The cargo ResourceUtils.
163      */
164     private ResourceUtils utils = new ResourceUtils();
165     
166     /**
167      * The archive manager.
168      * @component
169      */
170     private ArchiverManager archiverManager;
171     
172     /**
173      * Dependencies to be included in the WEB-INF/lib folder.
174      * @parameter
175      */
176     private final List libDependencies = new ArrayList();
177     
178     /**
179      * @plexus.requirement
180      */
181     private ArtifactFactory factory;
182     
183     /**
184      * @parameter expression="${component.org.apache.maven.artifact.installer.ArtifactInstaller}"
185      * @required
186      * @readonly
187      */
188     protected ArtifactInstaller installer;
189   
190     /**
191      * The file that we want to produce.
192      * @parameter
193      * @required
194      */
195     private File destFile;
196     
197  
198     /**
199      * @parameter expression="${localRepository}"
200      * @required
201      * @readonly
202      */
203     protected ArtifactRepository localRepository;
204     
205     /**
206      * GroupId of the artifact to be installed. Retrieved from POM file 
207      * if specified.
208      *
209      * @parameter expression="${project.groupId}"
210      */
211     protected String groupId;
212 
213     /**
214      * ArtifactId of the artifact to be installed. Retrieved from POM file 
215      * if specified.
216      *
217      * @parameter expression="${project.artifactId}"
218      */
219     protected String artifactId;
220 
221     /**
222      * Version of the artifact to be installed. Retrieved from POM file 
223      * if specified
224      *
225      * @parameter expression="${project.version}"
226      */
227     protected String projectVersion;
228 
229     /**
230      * Version of the web.xml to create.
231      *
232      * @parameter
233      */
234     protected String version = null;
235     
236     /**
237      * Packaging type of the artifact to be installed. Retrieved from POM file 
238      * if specified
239      *
240      * @parameter expression="${project.packaging}"
241      */
242     protected String packaging;
243 
244     /**
245      * Classifier type of the artifact to be installed.  For example, "sources" 
246      * or "javadoc".
247      * Defaults to none which means this is the project's main jar.
248      *
249      * @parameter expression="${project.classifier}"
250      */
251     protected String classifier;
252     
253     /**
254      * The "main" method of the mojo.
255      * @throws MojoExecutionException in case an error occurs.
256      * @throws MojoFailureException in case a failure occurs.
257      */
258     public void execute() throws MojoExecutionException, MojoFailureException
259 	{
260         if (this.srcFile != null) 
261         {
262             getLog().info("Analyzing war: " + this.srcFile.getAbsolutePath());
263         }
264         
265         WebXml webXml = null;
266         MavenArchiver archiver = new MavenArchiver();
267         archiver.setArchiver(warArchiver);
268         archiver.setOutputFile(destFile);
269         
270         File tmpWebXml = null;
271         File tempLocation = null;
272         
273         try 
274         {
275             if (srcFile != null) 
276             {
277                 webXml = getOriginalWebXml();
278                 if (webXml == null)
279                 {
280                     if (this.version == null)
281                     {
282                         throw new MojoExecutionException("Your source file "
283                            + "does not contain a web.xml. Please provide a "
284                            + "war with a web.xml or specify the [version] "
285                            + "attribute.");
286                     }
287                     WebXmlVersion webXmlVersion = null;
288                     if (this.version.equals("2.2"))
289                     {
290                         webXmlVersion = WebXmlVersion.V2_2;
291                     }
292                     else if (this.version.equals("2.3"))
293                     {
294                         webXmlVersion = WebXmlVersion.V2_3;
295                     } 
296                     else 
297                     {
298                         webXmlVersion = WebXmlVersion.V2_4;
299                     }
300                     webXml = WebXmlIo.newWebXml(webXmlVersion);
301                 }
302             }
303             else
304             {
305                 
306                 if (this.version == null)
307                 {
308                     throw new MojoExecutionException("You need to specify "
309                            + "either the [srcFile] or the [version] attribute");
310                 }
311                 else
312                 {
313                     WebXmlVersion webXmlVersion = null;
314                     if (this.version.equals("2.2"))
315                     {
316                         webXmlVersion = WebXmlVersion.V2_2;
317                     }
318                     else if (this.version.equals("2.3"))
319                     {
320                         webXmlVersion = WebXmlVersion.V2_3;
321                     } 
322                     else 
323                     {
324                         webXmlVersion = WebXmlVersion.V2_4;
325                     }
326                     webXml = WebXmlIo.newWebXml(webXmlVersion);
327                 }
328             }
329             tmpWebXml = cactifyWebXml(webXml);
330             
331             //Add the required libs for Cactus.
332             addJarWithClass("org.aspectj.lang.JoinPoint", 
333             "AspectJ Runtime");
334             addJarWithClass("org.apache.cactus."
335                    + "ServletTestCase", "Cactus Framework");
336             addJarWithClass("org.apache.commons.logging.Log",
337                 "Commons-Logging");
338             addJarWithClass("org.apache.commons."
339                    + "httpclient.HttpClient", "Commons-HttpClient");
340             addJarWithClass("junit.framework."
341                    + "TestCase", "JUnit");
342             
343             tempLocation = createTempFile("cactus", "explode.tmp.dir",
344                     getProject().getBasedir(), true);
345             tempLocation.mkdirs();
346             tempLocation.deleteOnExit();
347 
348             if (testClasses != null) 
349             {
350 	            //Add the classes.
351 	            warArchiver.addClasses(new File(testClasses.getDirectory()), 
352 	            								testClasses.getIncludesArray(),
353 	            								testClasses.getExcludesArray());
354             }
355 
356             //Now add all of the additional lib files.
357             for (Iterator iter = libDependencies.iterator(); iter.hasNext();)
358             {
359                 org.apache.cactus.maven2.mojos.Dependency dependency = 
360                     (org.apache.cactus.maven2.mojos.Dependency) iter.next();
361                 warArchiver.addLib(new File(dependency.getDependencyPath(
362                     project, getLog())));
363             }
364             
365             try 
366             {
367                 if(this.srcFile != null)
368                 {
369                     AssemblyFileUtils.unpack(this.srcFile, tempLocation,
370                         archiverManager);
371                 }
372             } 
373             catch (ArchiveExpansionException e) 
374             {
375                 throw new MojoExecutionException("Error extracting the"
376                        + " archive.", e);
377             } 
378             catch (NoSuchArchiverException e) 
379             {
380                 throw new MojoExecutionException("Problem reading the "
381                        + "source archive.", e);
382             }
383             warArchiver.addDirectory(tempLocation);
384             warArchiver.setWebxml(tmpWebXml);
385             archiver.createArchive(getProject(), getArchive());
386         } 
387         catch (ArchiverException e) 
388         {
389             throw new MojoExecutionException("Problem reading the "
390                    + "source archive.", e);
391         } 
392         catch (JDOMException e) 
393         {
394             throw new MojoExecutionException("Unable to cactify "
395                    + "your web.xml.", e);
396         }
397         catch (ManifestException e) 
398         {
399             throw new MojoExecutionException("Problem reading the "
400                    + "source archive.", e);
401         } 
402         catch (IOException e) 
403         {
404             throw new MojoExecutionException("Input/output error reading the"
405                    + "source archive.", e);
406         } 
407         catch (DependencyResolutionRequiredException e) 
408         {
409             throw new MojoExecutionException("Error resolving your "
410                    + "dependencies", e);
411         }
412         finally
413         {
414             try 
415             {
416                 if (tempLocation != null)
417                 {
418                     FileUtils.deleteDirectory(tempLocation);
419                 }
420             } 
421             catch (IOException e) 
422             {
423                 throw new MojoExecutionException("Error deleting temporary "
424                        + "folder", e);
425             }
426         }
427     }
428     
429     /**
430      * Enhances the provided web deployment descriptor with the definitions 
431      * required for testing with Cactus.
432      * 
433      * @param theWebXml The original deployment descriptor
434      * @return A temporary file containing the cactified descriptor
435      * @throws JDOMException in case a JDOM exception is thrown.
436      * @throws MojoExecutionException in case any other error occurs.
437      */
438     private File cactifyWebXml(WebXml theWebXml) throws JDOMException, 
439                                                         MojoExecutionException
440     {
441         CactifyUtils utils = new CactifyUtils();
442         utils.setLogger(createLogger());
443         utils.addRedirectorDefinitions(theWebXml, redirectors);
444         addJspRedirector();
445         addEjbRefs(theWebXml);
446         
447         // If the user has specified a deployment descriptor to merge into the
448         // cactified descriptor, perform the merge 
449         if (this.mergeWebXml != null)
450         {
451             try
452             {
453                 WebXml parsedMergeWebXml = WebXmlIo.parseWebXmlFromFile(
454                     this.mergeWebXml, this.xmlCatalog);
455                 WebXmlMerger merger = new WebXmlMerger(theWebXml);
456                 merger.setLogger(utils.getLogger());
457                
458                 merger.merge(parsedMergeWebXml);
459             }
460             catch (IOException e)
461             {
462                 throw new MojoExecutionException(
463                     "Could not merge deployment descriptors", e);
464             }
465         }
466         
467         // Serialize the cactified deployment descriptor into a temporary file,
468         // so that it can get picked up by the War task
469         //FileUtils fileUtils = FileUtils.newFileUtils();
470         File tmpDir = createTempFile("cactus", "tmp.dir",
471             new File("."), true);
472         tmpDir.mkdirs();
473         tmpDir.deleteOnExit();
474         File webXmlFile = null;
475         try
476         {
477             tmpDir.mkdir();
478             File[] files = WebXmlIo.writeAll(theWebXml, 
479                 tmpDir.getAbsolutePath());
480             List includes = new ArrayList();
481             for (int i = 0; i < files.length; i++)
482             {
483                 File f = files[i];
484                 f.deleteOnExit();
485                 if (f.getName().equals("web.xml"))
486                 {
487                     webXmlFile = f;
488                 }
489                 else
490                 {
491                     includes.add(f.getName());
492                 }
493             }
494             String[] strIncludes = new String[includes.size()];
495             int i = 0;
496             for (Iterator iter = includes.iterator(); iter.hasNext();)
497             {
498                 strIncludes[i] = iter.next().toString();
499                 i++;
500             }
501             try 
502             {
503                 warArchiver.addWebinf(tmpDir, strIncludes, null);
504             } 
505             catch (ArchiverException e) 
506             {
507                 throw new MojoExecutionException(
508                         "Error reading the source archive.", e);
509             }
510         }
511         catch (IOException ioe)
512         {
513             throw new MojoExecutionException(
514                 "Could not write temporary deployment descriptor", ioe);
515         }
516         return webXmlFile;
517     }
518     
519     /**
520      * A method to create the temporary files.
521      * 
522      * @param thePrefix the prefix of the filename.
523      * @param theSuffix the suffix of the filename
524      * @param theParentDir the parent directory
525      * @param isDeleteOnExit should we delete the directories on exit?
526      * @return the temporary file
527      */
528     public File createTempFile(String thePrefix, String theSuffix, 
529                                    File theParentDir, boolean isDeleteOnExit) 
530     {
531     File result = null;
532     String parent = (theParentDir == null)
533             ? System.getProperty("java.io.tmpdir")
534             : theParentDir.getPath();
535 
536         DecimalFormat fmt = new DecimalFormat("#####");
537         synchronized (rand) 
538         {
539             do 
540             {
541                 result = new File(parent,
542                    thePrefix + fmt.format(Math.abs(rand.nextInt()))
543                    + theSuffix);
544             } 
545             while (result.exists());
546         }
547         if (isDeleteOnExit) 
548         {
549             result.deleteOnExit();
550         }
551         return result;
552     }
553     
554 
555     /**
556      * Adds the Cactus JSP redirector file to the web application.
557      * @throws MojoExecutionException in case an error occurs
558      */
559     private void addJspRedirector() throws MojoExecutionException
560     {
561         // Now copy the actual JSP redirector file into the web application
562         File jspRedirectorFile = new File(
563             new File(System.getProperty("java.io.tmpdir")),
564             "jspRedirector.jsp");
565         jspRedirectorFile.deleteOnExit();
566         try
567         {
568             utils.copyResource("/org/apache/cactus/server/jspRedirector.jsp",
569                 jspRedirectorFile);
570         }
571         catch (IOException e)
572         {
573             getLog().warn("Could not copy the JSP redirector (" 
574                    + e.getMessage() + ")");
575         }
576         try 
577         {
578             warArchiver.addFile(jspRedirectorFile, jspRedirectorFile.getName());
579         } 
580         catch (ArchiverException e) 
581         {
582             throw new MojoExecutionException("Failed to add jsp redirector", e);
583         }
584     }
585 
586     /**
587      * Adds the JAR file containing the specified resource to the WEB-INF/lib
588      * folder of a web-application archive.
589      * 
590      * @param theClassName The name of the class that the JAR contains
591      * @param theDescription A description of the JAR that should be displayed
592      *        to the user in log messages
593      * @return File the jar file
594      * @throws ArchiverException in case an error occurs.
595      */
596     private File addJarWithClass(String theClassName, String theDescription)
597     throws ArchiverException
598     {
599         String resourceName = "/" + theClassName.replace('.', '/') + ".class";
600         if (srcFile != null)
601         {
602             try
603             {
604                 WarArchive srcWar = new DefaultWarArchive(
605                     new FileInputStream(srcFile));
606                 getLog().info("Inspecting..");
607                 if (srcWar.containsClass(theClassName))
608                 {
609                     getLog().info("The " + theDescription + " JAR is "
610                            + "already present in the WAR. Will skip.");
611                     return null;
612                 }
613             }
614             catch (IOException ioe)
615             {
616                 getLog().warn("Problem reading source WAR to when "
617                   + "trying to detect already present JAR files (" + ioe + ")");
618             }
619         }
620         File file = utils.getResourceLocation(resourceName);
621         
622         if (file != null)
623         {
624             getLog().info("Adding: " + file.getName());
625             warArchiver.addLib(file);
626         }
627 
628         return file;   
629     }
630     
631     /**
632      * Extracts and parses the original web deployment descriptor from the
633      * web-app.
634      * 
635      * @return The parsed descriptor or null if not found
636      * @throws MojoExecutionException If the descriptor could not be 
637      *         parsed
638      * @throws JDOMException in case is JDOM exception is thrown.
639      */
640     private WebXml getOriginalWebXml() throws MojoExecutionException, 
641                                               JDOMException
642     {
643         // Open the archive as JAR file and extract the deployment descriptor
644         WarArchive war = null;
645         try
646         {
647             war = new DefaultWarArchive(new FileInputStream(this.srcFile));
648             WebXml webXml = war.getWebXml();
649             return webXml;
650         }
651         catch (IOException e)
652         {
653             throw new MojoExecutionException("Failed to get the original "
654                     + "web.xml", e);
655         }
656     }
657     
658     /**
659      * Add ejb references to a web.xml.
660      * 
661      * @param theWebXml the web.xml to modify
662      */
663     private void addEjbRefs(WebXml theWebXml)
664     {
665         Iterator i = ejbRefs.iterator();
666         while (i.hasNext())
667         {
668             EjbRef ref = (EjbRef) i.next();
669             WebXmlUtils.addEjbRef(theWebXml, ref);
670         }
671     }
672     
673     /**
674      * Getter method for the MavenProject.
675      * @return the MavenProject
676      */
677     public MavenProject getProject()
678     {
679         return project;
680     }
681     
682     /**
683      * Setter method for the MavenProject.
684      * @param project
685      */
686     public void setProject(MavenProject theProject)
687     {
688         this.project = theProject;
689     }
690     
691     /**
692      * Getter method for the MavenArchiveConfiguration.
693      * @return the MavenArchiveConfiguration
694      */
695     public MavenArchiveConfiguration getArchive()
696     {
697         return archive;
698     }
699     
700     /**
701      * Create a logger. If a <code>&lt;log&gt;</code> configuration element 
702      * has been specified by the user then use it. If none is specified then 
703      * log to the Maven 2 logging subsystem.
704      *
705      * @return the logger to use for logging this plugin's activity
706      */
707     protected Logger createLogger()
708     {
709         Logger logger;
710             logger = new MavenLogger(getLog());
711         return logger;
712     }
713     
714     /**
715      * Returns the context.
716      * 
717      * @return <code>java.lang.String</code>
718      */
719     public String getContext() 
720     {
721         return context;
722     }
723     
724     /**
725      * Returns the source file for cactification.
726      * 
727      * @return <code>java.io.File</code>
728      */
729     public File getSrcFile() 
730     {
731         return this.srcFile;
732     }
733     
734 
735     /**
736      * Sets the context.
737      * 
738      * @param context
739      */
740     public void setContext(String theContext) 
741     {
742         this.context = theContext;
743     }
744     
745     /**
746      * Sets the web-app version to use when creating a WAR file from scratch.
747      * 
748      * @param theVersion The version
749      */
750     public final void setVersion(Version theVersion)
751     {
752         this.version = theVersion.getValue();
753     }
754     
755     /**
756      * Sets the source file for cactification.
757      * 
758      * @param theSrcFile The source file
759      */
760     public final void setSrcFile(File theSrcFile)
761     {
762         this.srcFile = theSrcFile;
763     }
764     
765     /**
766      * Gets the file name.
767      * 
768      * @return the name of the web app file
769      */
770     public String getFileName()
771     {
772         return FILE_NAME;
773     }
774     
775     /**
776      * Setter method for the destFile.
777      * 
778      * @param destFile
779      */
780     public void setDestFile(File theDestFile) 
781     {
782         this.destFile = theDestFile;
783     }
784     
785     /**
786      * Adds a configured EjbRef instance. Called by Ant.
787      * 
788      * @param theEjbRef the EjbRef to add
789      */
790     public final void addConfiguredEjbref(EjbRef theEjbRef)
791     {
792         ejbRefs.add(theEjbRef);
793     }
794 
795     /**
796      * Setter for the warArchiver so that the cactifyearmojo
797      * can set it.
798      * 
799      * @param warArchiver
800      */
801     public void setWarArchiver(WarArchiver theWarArchiver) 
802     {
803         this.warArchiver = theWarArchiver;
804     }
805 }