View Javadoc

1   package org.apache.tomcat.maven.plugin.tomcat7.run;
2   /*
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with 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,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   */
20  
21  import org.apache.commons.compress.archivers.ArchiveException;
22  import org.apache.commons.compress.archivers.ArchiveOutputStream;
23  import org.apache.commons.compress.archivers.ArchiveStreamFactory;
24  import org.apache.commons.compress.archivers.jar.JarArchiveEntry;
25  import org.apache.commons.io.FileUtils;
26  import org.apache.commons.io.IOUtils;
27  import org.apache.commons.lang.StringUtils;
28  import org.apache.maven.artifact.Artifact;
29  import org.apache.maven.artifact.factory.ArtifactFactory;
30  import org.apache.maven.artifact.repository.ArtifactRepository;
31  import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
32  import org.apache.maven.artifact.resolver.ArtifactResolutionException;
33  import org.apache.maven.artifact.resolver.ArtifactResolver;
34  import org.apache.maven.model.Dependency;
35  import org.apache.maven.plugin.MojoExecutionException;
36  import org.apache.maven.plugin.MojoFailureException;
37  import org.apache.maven.plugins.annotations.Component;
38  import org.apache.maven.plugins.annotations.Parameter;
39  import org.apache.maven.project.MavenProject;
40  import org.apache.maven.project.MavenProjectHelper;
41  import org.apache.tomcat.maven.plugin.tomcat7.AbstractTomcat7Mojo;
42  import org.apache.tomcat.maven.runner.Tomcat7Runner;
43  import org.apache.tomcat.maven.runner.Tomcat7RunnerCli;
44  import org.codehaus.plexus.archiver.jar.Manifest;
45  import org.codehaus.plexus.archiver.jar.ManifestException;
46  import org.codehaus.plexus.util.DirectoryScanner;
47  
48  import java.io.File;
49  import java.io.FileInputStream;
50  import java.io.FileNotFoundException;
51  import java.io.FileOutputStream;
52  import java.io.IOException;
53  import java.io.InputStream;
54  import java.io.OutputStream;
55  import java.io.PrintWriter;
56  import java.util.ArrayList;
57  import java.util.Enumeration;
58  import java.util.Iterator;
59  import java.util.List;
60  import java.util.Properties;
61  import java.util.jar.JarEntry;
62  import java.util.jar.JarFile;
63  
64  /**
65   * @author Olivier Lamy
66   * @since 2.0
67   */
68  public abstract class AbstractExecWarMojo
69      extends AbstractTomcat7Mojo
70  {
71  
72      @Parameter( defaultValue = "${project.artifact}", required = true, readonly = true )
73      protected Artifact projectArtifact;
74  
75      /**
76       * The maven project.
77       */
78      @Parameter( defaultValue = "${project}", required = true, readonly = true )
79      protected MavenProject project;
80  
81      @Parameter( defaultValue = "${plugin.artifacts}", required = true )
82      protected List<Artifact> pluginArtifacts;
83  
84      @Parameter( defaultValue = "${project.build.directory}" )
85      protected File buildDirectory;
86  
87      /**
88       * Path under {@link #buildDirectory} where this mojo may do temporary work.
89       */
90      @Parameter( defaultValue = "${project.build.directory}/tomcat7-maven-plugin-exec" )
91      private File pluginWorkDirectory;
92  
93      @Parameter( property = "maven.tomcat.exec.war.tomcatConf", defaultValue = "src/main/tomcatconf" )
94      protected File tomcatConfigurationFilesDirectory;
95  
96      @Parameter( defaultValue = "src/main/tomcatconf/server.xml", property = "maven.tomcat.exec.war.serverXml" )
97      protected File serverXml;
98  
99      /**
100      * Name of the generated exec JAR.
101      */
102     @Parameter( property = "tomcat.jar.finalName",
103                 defaultValue = "${project.artifactId}-${project.version}-war-exec.jar", required = true )
104     protected String finalName;
105 
106     /**
107      * The webapp context path to use for the web application being run.
108      * The name to store webapp in exec jar. Do not use /
109      */
110     @Parameter( property = "maven.tomcat.path", defaultValue = "${project.artifactId}", required = true )
111     protected String path;
112 
113     @Parameter
114     protected List<WarRunDependency> warRunDependencies;
115 
116     @Component
117     protected ArtifactResolver artifactResolver;
118 
119     /**
120      * Maven Artifact Factory component.
121      */
122     @Component
123     protected ArtifactFactory artifactFactory;
124 
125     /**
126      * Location of the local repository.
127      */
128     @Parameter( defaultValue = "${localRepository}", required = true, readonly = true )
129     protected ArtifactRepository local;
130 
131     /**
132      * List of Remote Repositories used by the resolver
133      */
134     @Parameter( defaultValue = "${project.remoteArtifactRepositories}", required = true, readonly = true )
135     protected List<ArtifactRepository> remoteRepos;
136 
137     @Component
138     protected MavenProjectHelper projectHelper;
139 
140     /**
141      * Attach or not the generated artifact to the build (use true if you want to install or deploy it)
142      */
143     @Parameter( property = "maven.tomcat.exec.war.attachArtifact", defaultValue = "true", required = true )
144     protected boolean attachArtifact;
145 
146 
147     /**
148      * the classifier to use for the attached/generated artifact
149      */
150     @Parameter( property = "maven.tomcat.exec.war.attachArtifactClassifier", defaultValue = "exec-war",
151                 required = true )
152     protected String attachArtifactClassifier;
153 
154 
155     /**
156      * the type to use for the attached/generated artifact
157      */
158     @Parameter( property = "maven.tomcat.exec.war.attachArtifactType", defaultValue = "jar", required = true )
159     protected String attachArtifactClassifierType;
160 
161     /**
162      * to enable naming when starting tomcat
163      */
164     @Parameter( property = "maven.tomcat.exec.war.enableNaming", defaultValue = "false", required = true )
165     protected boolean enableNaming;
166 
167     /**
168      * see http://tomcat.apache.org/tomcat-7.0-doc/config/valve.html
169      */
170     @Parameter( property = "maven.tomcat.exec.war.accessLogValveFormat", defaultValue = "%h %l %u %t %r %s %b %I %D",
171                 required = true )
172     protected String accessLogValveFormat;
173 
174     /**
175      * list of extra dependencies to add in the standalone tomcat jar: your jdbc driver, mail.jar etc..
176      * <b>Those dependencies will be in root classloader.</b>
177      */
178     @Parameter
179     protected List<ExtraDependency> extraDependencies;
180 
181     /**
182      * list of extra resources to add in the standalone tomcat jar: your logger configuration etc
183      */
184     @Parameter
185     protected List<ExtraResource> extraResources;
186 
187     /**
188      * Main class to use for starting the standalone jar.
189      */
190     @Parameter( property = "maven.tomcat.exec.war.mainClass",
191                 defaultValue = "org.apache.tomcat.maven.runner.Tomcat7RunnerCli", required = true )
192     protected String mainClass;
193 
194     /**
195      * which connector protocol to use HTTP/1.1 or org.apache.coyote.http11.Http11NioProtocol
196      */
197     @Parameter( property = "maven.tomcat.exec.war.connectorHttpProtocol", defaultValue = "HTTP/1.1", required = true )
198     protected String connectorHttpProtocol;
199 
200     public void execute()
201         throws MojoExecutionException, MojoFailureException
202     {
203 
204         //project.addAttachedArtifact(  );
205         File warExecFile = new File( buildDirectory, finalName );
206         if ( warExecFile.exists() )
207         {
208             warExecFile.delete();
209         }
210 
211         File execWarJar = new File( buildDirectory, finalName );
212 
213         FileOutputStream execWarJarOutputStream = null;
214         ArchiveOutputStream os = null;
215         File tmpPropertiesFile = null;
216         File tmpManifestFile = null;
217         FileOutputStream tmpPropertiesFileOutputStream = null;
218         PrintWriter tmpManifestWriter = null;
219 
220         try
221         {
222 
223             tmpPropertiesFile = new File( buildDirectory, "war-exec.properties" );
224             if ( tmpPropertiesFile.exists() )
225             {
226                 tmpPropertiesFile.delete();
227             }
228             tmpPropertiesFile.getParentFile().mkdirs();
229 
230             tmpManifestFile = new File( buildDirectory, "war-exec.manifest" );
231             if ( tmpManifestFile.exists() )
232             {
233                 tmpManifestFile.delete();
234             }
235             tmpPropertiesFileOutputStream = new FileOutputStream( tmpPropertiesFile );
236             execWarJar.getParentFile().mkdirs();
237             execWarJar.createNewFile();
238             execWarJarOutputStream = new FileOutputStream( execWarJar );
239 
240             tmpManifestWriter = new PrintWriter( tmpManifestFile );
241 
242             // store :
243             //* wars in the root: foo.war
244             //* tomcat jars
245             //* file tomcat.standalone.properties with possible values :
246             //   * useServerXml=true/false to use directly the one provided
247             //   * enableNaming=true/false
248             //   * wars=foo.war|contextpath;bar.war  ( |contextpath is optionnal if empty use the war name )
249             //   * accessLogValveFormat=
250             //   * connectorhttpProtocol: HTTP/1.1 or org.apache.coyote.http11.Http11NioProtocol
251             //* optionnal: conf/ with usual tomcat configuration files
252             //* MANIFEST with Main-Class
253 
254             Properties properties = new Properties();
255 
256             properties.put( Tomcat7Runner.ARCHIVE_GENERATION_TIMESTAMP_KEY,
257                             Long.toString( System.currentTimeMillis() ) );
258             properties.put( Tomcat7Runner.ENABLE_NAMING_KEY, Boolean.toString( enableNaming ) );
259             properties.put( Tomcat7Runner.ACCESS_LOG_VALVE_FORMAT_KEY, accessLogValveFormat );
260             properties.put( Tomcat7Runner.HTTP_PROTOCOL_KEY, connectorHttpProtocol );
261 
262             os = new ArchiveStreamFactory().createArchiveOutputStream( ArchiveStreamFactory.JAR,
263                                                                        execWarJarOutputStream );
264 
265             if ( "war".equals( project.getPackaging() ) )
266             {
267 
268                 os.putArchiveEntry( new JarArchiveEntry( StringUtils.removeStart( path, "/" ) + ".war" ) );
269                 IOUtils.copy( new FileInputStream( projectArtifact.getFile() ), os );
270                 os.closeArchiveEntry();
271 
272                 properties.put( Tomcat7Runner.WARS_KEY, StringUtils.removeStart( path, "/" ) + ".war|" + path );
273             }
274             else if ( warRunDependencies != null && !warRunDependencies.isEmpty() )
275             {
276                 for ( WarRunDependency warRunDependency : warRunDependencies )
277                 {
278                     if ( warRunDependency.dependency != null )
279                     {
280                         Dependency dependency = warRunDependency.dependency;
281                         Artifact artifact = artifactFactory.createArtifactWithClassifier( dependency.getGroupId(),
282                                                                                           dependency.getArtifactId(),
283                                                                                           dependency.getVersion(),
284                                                                                           dependency.getType(),
285                                                                                           dependency.getClassifier() );
286 
287                         artifactResolver.resolve( artifact, this.remoteRepos, this.local );
288 
289                         File warFileToBundle = new File( resolvePluginWorkDir(), artifact.getFile().getName() );
290                         FileUtils.copyFile( artifact.getFile(), warFileToBundle );
291 
292                         if ( warRunDependency.contextXml != null )
293                         {
294                             warFileToBundle = addContextXmlToWar( warRunDependency.contextXml, warFileToBundle );
295                         }
296                         final String warFileName = artifact.getFile().getName();
297                         os.putArchiveEntry( new JarArchiveEntry( warFileName ) );
298                         IOUtils.copy( new FileInputStream( warFileToBundle ), os );
299                         os.closeArchiveEntry();
300                         String propertyWarValue = properties.getProperty( Tomcat7Runner.WARS_KEY );
301                         String contextPath =
302                             StringUtils.isEmpty( warRunDependency.contextPath ) ? "/" : warRunDependency.contextPath;
303                         if ( propertyWarValue != null )
304                         {
305                             properties.put( Tomcat7Runner.WARS_KEY,
306                                             propertyWarValue + ";" + warFileName + "|" + contextPath );
307                         }
308                         else
309                         {
310                             properties.put( Tomcat7Runner.WARS_KEY, warFileName + "|" + contextPath );
311                         }
312                     }
313                 }
314             }
315 
316             if ( serverXml != null && serverXml.exists() )
317             {
318                 os.putArchiveEntry( new JarArchiveEntry( "conf/server.xml" ) );
319                 IOUtils.copy( new FileInputStream( serverXml ), os );
320                 os.closeArchiveEntry();
321                 properties.put( Tomcat7Runner.USE_SERVER_XML_KEY, Boolean.TRUE.toString() );
322             }
323             else
324             {
325                 properties.put( Tomcat7Runner.USE_SERVER_XML_KEY, Boolean.FALSE.toString() );
326             }
327 
328             os.putArchiveEntry( new JarArchiveEntry( "conf/web.xml" ) );
329             IOUtils.copy( getClass().getResourceAsStream( "/conf/web.xml" ), os );
330             os.closeArchiveEntry();
331 
332             properties.store( tmpPropertiesFileOutputStream, "created by Apache Tomcat Maven plugin" );
333 
334             tmpPropertiesFileOutputStream.flush();
335             tmpPropertiesFileOutputStream.close();
336 
337             os.putArchiveEntry( new JarArchiveEntry( Tomcat7RunnerCli.STAND_ALONE_PROPERTIES_FILENAME ) );
338             IOUtils.copy( new FileInputStream( tmpPropertiesFile ), os );
339             os.closeArchiveEntry();
340 
341             // add tomcat classes
342             for ( Artifact pluginArtifact : pluginArtifacts )
343             {
344                 if ( StringUtils.equals( "org.apache.tomcat", pluginArtifact.getGroupId() ) || StringUtils.equals(
345                     "org.apache.tomcat.embed", pluginArtifact.getGroupId() ) || StringUtils.equals(
346                     "org.eclipse.jdt.core.compiler", pluginArtifact.getGroupId() ) || StringUtils.equals( "commons-cli",
347                                                                                                           pluginArtifact.getArtifactId() )
348                     || StringUtils.equals( "tomcat7-war-runner", pluginArtifact.getArtifactId() ) )
349                 {
350                     JarFile jarFile = new JarFile( pluginArtifact.getFile() );
351                     extractJarToArchive( jarFile, os );
352                 }
353             }
354 
355             // add extra dependencies
356             if ( extraDependencies != null && !extraDependencies.isEmpty() )
357             {
358                 for ( Dependency dependency : extraDependencies )
359                 {
360                     // String groupId, String artifactId, String version, String scope, String type
361                     Artifact artifact =
362                         artifactFactory.createArtifact( dependency.getGroupId(), dependency.getArtifactId(),
363                                                         dependency.getVersion(), dependency.getScope(),
364                                                         dependency.getType() );
365 
366                     artifactResolver.resolve( artifact, this.remoteRepos, this.local );
367                     JarFile jarFile = new JarFile( artifact.getFile() );
368                     extractJarToArchive( jarFile, os );
369                 }
370             }
371 
372             Manifest manifest = new Manifest();
373 
374             Manifest.Attribute mainClassAtt = new Manifest.Attribute();
375             mainClassAtt.setName( "Main-Class" );
376             mainClassAtt.setValue( mainClass );
377             manifest.addConfiguredAttribute( mainClassAtt );
378 
379             manifest.write( tmpManifestWriter );
380             tmpManifestWriter.flush();
381             tmpManifestWriter.close();
382 
383             os.putArchiveEntry( new JarArchiveEntry( "META-INF/MANIFEST.MF" ) );
384             IOUtils.copy( new FileInputStream( tmpManifestFile ), os );
385             os.closeArchiveEntry();
386 
387             if ( attachArtifact )
388             {
389                 //MavenProject project, String artifactType, String artifactClassifier, File artifactFile
390                 projectHelper.attachArtifact( project, attachArtifactClassifierType, attachArtifactClassifier,
391                                               execWarJar );
392             }
393 
394             if ( extraResources != null )
395             {
396                 for ( ExtraResource extraResource : extraResources )
397                 {
398 
399                     DirectoryScanner directoryScanner = new DirectoryScanner();
400                     directoryScanner.setBasedir( extraResource.getDirectory() );
401                     directoryScanner.addDefaultExcludes();
402                     directoryScanner.setExcludes( toStringArray( extraResource.getExcludes() ) );
403                     directoryScanner.setIncludes( toStringArray( extraResource.getIncludes() ) );
404                     directoryScanner.scan();
405                     for ( String includeFile : directoryScanner.getIncludedFiles() )
406                     {
407                         getLog().debug( "include file:" + includeFile );
408                         os.putArchiveEntry( new JarArchiveEntry( includeFile ) );
409                         IOUtils.copy( new FileInputStream( new File( extraResource.getDirectory(), includeFile ) ),
410                                       os );
411                         os.closeArchiveEntry();
412                     }
413                 }
414             }
415 
416             if ( tomcatConfigurationFilesDirectory != null && tomcatConfigurationFilesDirectory.exists() )
417             {
418                 // Because its the tomcat default dir for configs
419                 String aConfigOutputDir = "conf/";
420                 copyDirectoryContentIntoArchive( tomcatConfigurationFilesDirectory, aConfigOutputDir, os );
421             }
422 
423         }
424         catch ( ManifestException e )
425         {
426             throw new MojoExecutionException( e.getMessage(), e );
427         }
428         catch ( IOException e )
429         {
430             throw new MojoExecutionException( e.getMessage(), e );
431         }
432         catch ( ArchiveException e )
433         {
434             throw new MojoExecutionException( e.getMessage(), e );
435         }
436         catch ( ArtifactNotFoundException e )
437         {
438             throw new MojoExecutionException( e.getMessage(), e );
439         }
440         catch ( ArtifactResolutionException e )
441         {
442             throw new MojoExecutionException( e.getMessage(), e );
443         }
444         finally
445         {
446             IOUtils.closeQuietly( os );
447             IOUtils.closeQuietly( tmpManifestWriter );
448             IOUtils.closeQuietly( execWarJarOutputStream );
449             IOUtils.closeQuietly( tmpPropertiesFileOutputStream );
450         }
451     }
452 
453     protected void copyDirectoryContentIntoArchive( File pSourceFolder, String pDestinationPath,
454                                                     ArchiveOutputStream pArchiveOutputSteam )
455         throws FileNotFoundException, IOException
456     {
457 
458         // Scan the directory
459         DirectoryScanner directoryScanner = new DirectoryScanner();
460         directoryScanner.setBasedir( pSourceFolder );
461         directoryScanner.addDefaultExcludes();
462         directoryScanner.scan();
463 
464         // Each File
465         for ( String aIncludeFileName : directoryScanner.getIncludedFiles() )
466         {
467             getLog().debug( "include configuration file : " + pDestinationPath + aIncludeFileName );
468             File aInputFile = new File( pSourceFolder, aIncludeFileName );
469 
470             FileInputStream aSourceFileInputStream = new FileInputStream( aInputFile );
471 
472             pArchiveOutputSteam.putArchiveEntry( new JarArchiveEntry( pDestinationPath + aIncludeFileName ) );
473             IOUtils.copy( aSourceFileInputStream, pArchiveOutputSteam );
474             pArchiveOutputSteam.closeArchiveEntry();
475 
476         }
477 
478     }
479 
480     /**
481      * Resolves the plugin work dir as a sub directory of {@link #buildDirectory}, creating it if it does not exist.
482      *
483      * @return File representing the resolved plugin work dir
484      * @throws MojoExecutionException if the plugin work dir cannot be created
485      */
486     protected File resolvePluginWorkDir()
487         throws MojoExecutionException
488     {
489         if ( !pluginWorkDirectory.exists() && !pluginWorkDirectory.mkdirs() )
490         {
491             throw new MojoExecutionException(
492                 "Could not create plugin work directory at " + pluginWorkDirectory.getAbsolutePath() );
493         }
494 
495         return pluginWorkDirectory;
496 
497     }
498 
499     protected String[] toStringArray( List list )
500     {
501         if ( list == null || list.isEmpty() )
502         {
503             return new String[0];
504         }
505         List<String> res = new ArrayList<String>( list.size() );
506 
507         for ( Iterator ite = list.iterator(); ite.hasNext(); )
508         {
509             res.add( (String) ite.next() );
510         }
511         return res.toArray( new String[res.size()] );
512     }
513 
514 
515     /**
516      * return file can be deleted
517      */
518     protected File addContextXmlToWar( File contextXmlFile, File warFile )
519         throws IOException, ArchiveException
520     {
521         ArchiveOutputStream os = null;
522         OutputStream warOutputStream = null;
523         File tmpWar = File.createTempFile( "tomcat", "war-exec" );
524         tmpWar.deleteOnExit();
525 
526         try
527         {
528             warOutputStream = new FileOutputStream( tmpWar );
529             os = new ArchiveStreamFactory().createArchiveOutputStream( ArchiveStreamFactory.JAR, warOutputStream );
530             os.putArchiveEntry( new JarArchiveEntry( "META-INF/context.xml" ) );
531             IOUtils.copy( new FileInputStream( contextXmlFile ), os );
532             os.closeArchiveEntry();
533 
534             JarFile jarFile = new JarFile( warFile );
535             extractJarToArchive( jarFile, os );
536             os.flush();
537         }
538         finally
539         {
540             IOUtils.closeQuietly( os );
541             IOUtils.closeQuietly( warOutputStream );
542         }
543         return tmpWar;
544     }
545 
546     /**
547      * Copy the contents of a jar file to another archive
548      *
549      * @param file The input jar file
550      * @param os   The output archive
551      * @throws IOException
552      */
553     protected void extractJarToArchive( JarFile file, ArchiveOutputStream os )
554         throws IOException
555     {
556         Enumeration<? extends JarEntry> entries = file.entries();
557         while ( entries.hasMoreElements() )
558         {
559             JarEntry j = entries.nextElement();
560             os.putArchiveEntry( new JarArchiveEntry( j.getName() ) );
561             IOUtils.copy( file.getInputStream( j ), os );
562             os.closeArchiveEntry();
563         }
564         if ( file != null )
565         {
566             file.close();
567         }
568     }
569 }