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.project.MavenProject;
38  import org.apache.maven.project.MavenProjectHelper;
39  import org.apache.tomcat.maven.plugin.tomcat7.AbstractTomcat7Mojo;
40  import org.apache.tomcat.maven.runner.Tomcat7Runner;
41  import org.apache.tomcat.maven.runner.Tomcat7RunnerCli;
42  import org.codehaus.plexus.archiver.jar.Manifest;
43  import org.codehaus.plexus.archiver.jar.ManifestException;
44  import org.codehaus.plexus.util.DirectoryScanner;
45  
46  import java.io.File;
47  import java.io.FileInputStream;
48  import java.io.FileOutputStream;
49  import java.io.IOException;
50  import java.io.InputStream;
51  import java.io.OutputStream;
52  import java.io.PrintWriter;
53  import java.util.ArrayList;
54  import java.util.Enumeration;
55  import java.util.Iterator;
56  import java.util.List;
57  import java.util.Properties;
58  import java.util.jar.JarEntry;
59  import java.util.jar.JarFile;
60  
61  /**
62   * @author Olivier Lamy
63   * @since 2.0
64   */
65  public abstract class AbstractExecWarMojo
66      extends AbstractTomcat7Mojo
67  {
68  
69  
70      /**
71       * @parameter default-value="${project.artifact}"
72       * @required
73       * @readonly
74       */
75      private Artifact projectArtifact;
76  
77      /**
78       * The maven project.
79       *
80       * @parameter default-value="${project}"
81       * @required
82       * @readonly
83       */
84      protected MavenProject project;
85  
86      /**
87       * @parameter default-value="${plugin.artifacts}"
88       * @required
89       */
90      private List<Artifact> pluginArtifacts;
91  
92      /**
93       * @parameter default-value="${project.build.directory}"
94       */
95      private File buildDirectory;
96  
97      /**
98       * @parameter default-value="src/main/tomcatconf" expression="${maven.tomcat.exec.war.tomcatConf}"
99       */
100     private File tomcatConfigurationFilesDirectory;
101 
102     /**
103      * @parameter default-value="src/main/tomcatconf/server.xml" expression="${maven.tomcat.exec.war.serverXml}"
104      */
105     private File serverXml;
106 
107 
108     /**
109      * Name of the generated exec JAR.
110      *
111      * @parameter expression="${tomcat.jar.finalName}" default-value="${project.artifactId}-${project.version}-war-exec.jar"
112      * @required
113      */
114     private String finalName;
115 
116     /**
117      * The webapp context path to use for the web application being run.
118      * The name to store webapp in exec jar. Do not use /
119      *
120      * @parameter expression="${maven.tomcat.path}" default-value="${project.artifactId}"
121      * @required
122      */
123     protected String path;
124 
125     /**
126      * @parameter
127      */
128     protected List<WarRunDependency> warRunDependencies;
129 
130     /**
131      * @component
132      */
133     protected ArtifactResolver artifactResolver;
134 
135     /**
136      * Maven Artifact Factory component.
137      *
138      * @component
139      */
140     private ArtifactFactory artifactFactory;
141 
142     /**
143      * Location of the local repository.
144      *
145      * @parameter expression="${localRepository}"
146      * @readonly
147      * @required
148      */
149     private ArtifactRepository local;
150 
151     /**
152      * List of Remote Repositories used by the resolver
153      *
154      * @parameter expression="${project.remoteArtifactRepositories}"
155      * @readonly
156      * @required
157      */
158     protected List<ArtifactRepository> remoteRepos;
159 
160     /**
161      * @component
162      * @required
163      * @readonly
164      */
165     private MavenProjectHelper projectHelper;
166 
167     /**
168      * Attach or not the generated artifact to the build (use true if you want to install or deploy it)
169      *
170      * @parameter expression="${maven.tomcat.exec.war.attachArtifact}" default-value="true"
171      * @required
172      */
173     private boolean attachArtifact;
174 
175 
176     /**
177      * the classifier to use for the attached/generated artifact
178      *
179      * @parameter expression="${maven.tomcat.exec.war.attachArtifactClassifier}" default-value="exec-war"
180      * @required
181      */
182     private String attachArtifactClassifier;
183 
184 
185     /**
186      * the type to use for the attached/generated artifact
187      *
188      * @parameter expression="${maven.tomcat.exec.war.attachArtifactType}" default-value="jar"
189      * @required
190      */
191     private String attachArtifactClassifierType;
192 
193     /**
194      * to enable naming when starting tomcat
195      *
196      * @parameter expression="${maven.tomcat.exec.war.enableNaming}" default-value="false"
197      * @required
198      */
199     private boolean enableNaming;
200 
201     /**
202      * see http://tomcat.apache.org/tomcat-7.0-doc/config/valve.html
203      *
204      * @parameter expression="${maven.tomcat.exec.war.accessLogValveFormat}" default-value="%h %l %u %t "%r" %s %b %I %D"
205      * @required
206      */
207     private String accessLogValveFormat;
208 
209     /**
210      * list of extra dependencies to add in the standalone tomcat jar: your jdbc driver, mail.jar etc..
211      * <b>Those dependencies will be in root classloader.</b>
212      *
213      * @parameter
214      */
215     private List<ExtraDependency> extraDependencies;
216 
217     /**
218      * list of extra resources to add in the standalone tomcat jar: your logger configuration etc
219      *
220      * @parameter
221      */
222     private List<ExtraResource> extraResources;
223 
224     /**
225      * Main class to use for starting the standalone jar.
226      *
227      * @parameter expression="${maven.tomcat.exec.war.mainClass}" default-value="org.apache.tomcat.maven.runner.Tomcat7RunnerCli"
228      * @required
229      */
230     private String mainClass;
231 
232     /**
233      * which connector protocol to use HTTP/1.1 or org.apache.coyote.http11.Http11NioProtocol
234      *
235      * @parameter expression="${maven.tomcat.exec.war.connectorHttpProtocol}" default-value="HTTP/1.1"
236      * @required
237      */
238     private String connectorHttpProtocol;
239 
240     public void execute()
241         throws MojoExecutionException, MojoFailureException
242     {
243 
244         //project.addAttachedArtifact(  );
245         File warExecFile = new File( buildDirectory, finalName );
246         if ( warExecFile.exists() )
247         {
248             warExecFile.delete();
249         }
250 
251         File execWarJar = new File( buildDirectory, finalName );
252 
253         FileOutputStream execWarJarOutputStream = null;
254         ArchiveOutputStream os = null;
255         File tmpPropertiesFile = null;
256         File tmpManifestFile = null;
257         FileOutputStream tmpPropertiesFileOutputStream = null;
258         PrintWriter tmpManifestWriter = null;
259 
260         try
261         {
262 
263             tmpPropertiesFile = new File( buildDirectory, "war-exec.properties" );
264             if ( tmpPropertiesFile.exists() )
265             {
266                 tmpPropertiesFile.delete();
267             }
268             tmpPropertiesFile.getParentFile().mkdirs();
269 
270             tmpManifestFile = new File( buildDirectory, "war-exec.manifest" );
271             if ( tmpManifestFile.exists() )
272             {
273                 tmpManifestFile.delete();
274             }
275             tmpPropertiesFileOutputStream = new FileOutputStream( tmpPropertiesFile );
276             execWarJar.getParentFile().mkdirs();
277             execWarJar.createNewFile();
278             execWarJarOutputStream = new FileOutputStream( execWarJar );
279 
280             tmpManifestWriter = new PrintWriter( tmpManifestFile );
281 
282             // store :
283             //* wars in the root: foo.war
284             //* tomcat jars
285             //* file tomcat.standalone.properties with possible values :
286             //   * useServerXml=true/false to use directly the one provided
287             //   * enableNaming=true/false
288             //   * wars=foo.war|contextpath;bar.war  ( |contextpath is optionnal if empty use the war name )
289             //   * accessLogValveFormat=
290             //   * connectorhttpProtocol: HTTP/1.1 or org.apache.coyote.http11.Http11NioProtocol
291             //* optionnal: conf/ with usual tomcat configuration files
292             //* MANIFEST with Main-Class
293 
294             Properties properties = new Properties();
295 
296             properties.put( Tomcat7Runner.ENABLE_NAMING_KEY, Boolean.toString( enableNaming ) );
297             properties.put( Tomcat7Runner.ACCESS_LOG_VALVE_FORMAT_KEY, accessLogValveFormat );
298             properties.put( Tomcat7Runner.HTTP_PROTOCOL_KEY, connectorHttpProtocol );
299 
300             os = new ArchiveStreamFactory().createArchiveOutputStream( ArchiveStreamFactory.JAR,
301                                                                        execWarJarOutputStream );
302 
303             if ( "war".equals( project.getPackaging() ) )
304             {
305 
306                 os.putArchiveEntry( new JarArchiveEntry( StringUtils.removeStart( path, "/" ) + ".war" ) );
307                 IOUtils.copy( new FileInputStream( projectArtifact.getFile() ), os );
308                 os.closeArchiveEntry();
309                 properties.put( Tomcat7Runner.WARS_KEY, StringUtils.removeStart( path, "/" ) + ".war|" + path );
310             }
311 
312             if ( "pom".equals( project.getPackaging() ) && ( warRunDependencies != null
313                 && !warRunDependencies.isEmpty() ) )
314             {
315                 for ( WarRunDependency warRunDependency : warRunDependencies )
316                 {
317                     if ( warRunDependency.dependency != null )
318                     {
319                         Dependency dependency = warRunDependency.dependency;
320                         // String groupId, String artifactId, String version, String scope, String type
321                         Artifact artifact =
322                             artifactFactory.createArtifact( dependency.getGroupId(), dependency.getArtifactId(),
323                                                             dependency.getVersion(), dependency.getScope(),
324                                                             dependency.getType() );
325 
326                         artifactResolver.resolve( artifact, this.remoteRepos, this.local );
327                         File warFile = new File( buildDirectory, artifact.getFile().getName() );
328                         String warFileName = artifact.getFile().getName();
329                         FileUtils.copyFile( artifact.getFile(), warFile );
330                         if ( warRunDependency.contextXml != null )
331                         {
332                             warFile = addContextXmlToWar( warRunDependency.contextXml, warFile );
333                         }
334                         os.putArchiveEntry( new JarArchiveEntry( warFileName ) );
335                         IOUtils.copy( new FileInputStream( warFile ), os );
336                         os.closeArchiveEntry();
337                         String propertyWarValue = properties.getProperty( Tomcat7Runner.WARS_KEY );
338                         String contextPath =
339                             StringUtils.isEmpty( warRunDependency.contextPath ) ? "/" : warRunDependency.contextPath;
340                         if ( propertyWarValue != null )
341                         {
342                             properties.put( Tomcat7Runner.WARS_KEY,
343                                             propertyWarValue + ";" + warFileName + "|" + contextPath );
344                         }
345                         else
346                         {
347                             properties.put( Tomcat7Runner.WARS_KEY, warFileName + "|" + contextPath );
348                         }
349                     }
350                 }
351             }
352 
353             // FIXME if no war has been added here we must stop with a human readable and user friendly error message
354 
355             if ( serverXml != null && serverXml.exists() )
356             {
357                 os.putArchiveEntry( new JarArchiveEntry( "conf/server.xml" ) );
358                 IOUtils.copy( new FileInputStream( serverXml ), os );
359                 os.closeArchiveEntry();
360                 properties.put( Tomcat7Runner.USE_SERVER_XML_KEY, Boolean.TRUE.toString() );
361             }
362             else
363             {
364                 properties.put( Tomcat7Runner.USE_SERVER_XML_KEY, Boolean.FALSE.toString() );
365             }
366 
367             properties.store( tmpPropertiesFileOutputStream, "created by Apache Tomcat Maven plugin" );
368 
369             tmpPropertiesFileOutputStream.flush();
370             tmpPropertiesFileOutputStream.close();
371 
372             os.putArchiveEntry( new JarArchiveEntry( Tomcat7RunnerCli.STAND_ALONE_PROPERTIES_FILENAME ) );
373             IOUtils.copy( new FileInputStream( tmpPropertiesFile ), os );
374             os.closeArchiveEntry();
375 
376             // add tomcat classes
377             for ( Artifact pluginArtifact : pluginArtifacts )
378             {
379                 if ( StringUtils.equals( "org.apache.tomcat", pluginArtifact.getGroupId() ) || StringUtils.equals(
380                     "org.apache.tomcat.embed", pluginArtifact.getGroupId() ) || StringUtils.equals(
381                     "org.eclipse.jdt.core.compiler", pluginArtifact.getGroupId() ) || StringUtils.equals( "commons-cli",
382                                                                                                           pluginArtifact.getArtifactId() )
383                     || StringUtils.equals( "tomcat7-war-runner", pluginArtifact.getArtifactId() ) )
384                 {
385                     JarFile jarFile = new JarFile( pluginArtifact.getFile() );
386                     Enumeration<JarEntry> jarEntries = jarFile.entries();
387                     while ( jarEntries.hasMoreElements() )
388                     {
389                         JarEntry jarEntry = jarEntries.nextElement();
390                         InputStream jarEntryIs = jarFile.getInputStream( jarEntry );
391 
392                         os.putArchiveEntry( new JarArchiveEntry( jarEntry.getName() ) );
393                         IOUtils.copy( jarEntryIs, os );
394                         os.closeArchiveEntry();
395                     }
396                 }
397             }
398 
399             // add extra dependencies
400             if ( extraDependencies != null && !extraDependencies.isEmpty() )
401             {
402                 for ( Dependency dependency : extraDependencies )
403                 {
404                     // String groupId, String artifactId, String version, String scope, String type
405                     Artifact artifact =
406                         artifactFactory.createArtifact( dependency.getGroupId(), dependency.getArtifactId(),
407                                                         dependency.getVersion(), dependency.getScope(),
408                                                         dependency.getType() );
409 
410                     artifactResolver.resolve( artifact, this.remoteRepos, this.local );
411                     JarFile jarFile = new JarFile( artifact.getFile() );
412                     Enumeration<JarEntry> jarEntries = jarFile.entries();
413                     while ( jarEntries.hasMoreElements() )
414                     {
415                         JarEntry jarEntry = jarEntries.nextElement();
416                         InputStream jarEntryIs = jarFile.getInputStream( jarEntry );
417 
418                         os.putArchiveEntry( new JarArchiveEntry( jarEntry.getName() ) );
419                         IOUtils.copy( jarEntryIs, os );
420                         os.closeArchiveEntry();
421                     }
422                 }
423             }
424 
425             Manifest manifest = new Manifest();
426 
427             Manifest.Attribute mainClassAtt = new Manifest.Attribute();
428             mainClassAtt.setName( "Main-Class" );
429             mainClassAtt.setValue( mainClass );
430             manifest.addConfiguredAttribute( mainClassAtt );
431 
432             manifest.write( tmpManifestWriter );
433             tmpManifestWriter.flush();
434             tmpManifestWriter.close();
435 
436             os.putArchiveEntry( new JarArchiveEntry( "META-INF/MANIFEST.MF" ) );
437             IOUtils.copy( new FileInputStream( tmpManifestFile ), os );
438             os.closeArchiveEntry();
439 
440             if ( attachArtifact )
441             {
442                 //MavenProject project, String artifactType, String artifactClassifier, File artifactFile
443                 projectHelper.attachArtifact( project, attachArtifactClassifierType, attachArtifactClassifier,
444                                               execWarJar );
445             }
446 
447             if ( extraResources != null )
448             {
449                 for ( ExtraResource extraResource : extraResources )
450                 {
451 
452                     DirectoryScanner directoryScanner = new DirectoryScanner();
453                     directoryScanner.setBasedir( extraResource.getDirectory() );
454                     directoryScanner.addDefaultExcludes();
455                     directoryScanner.setExcludes( toStringArray( extraResource.getExcludes() ) );
456                     directoryScanner.setIncludes( toStringArray( extraResource.getIncludes() ) );
457                     directoryScanner.scan();
458                     for ( String includeFile : directoryScanner.getIncludedFiles() )
459                     {
460                         getLog().debug( "include file:" + includeFile );
461                         os.putArchiveEntry( new JarArchiveEntry( includeFile ) );
462                         IOUtils.copy( new FileInputStream( new File( extraResource.getDirectory(), includeFile ) ),
463                                       os );
464                         os.closeArchiveEntry();
465                     }
466                 }
467             }
468 
469         }
470         catch ( ManifestException e )
471         {
472             throw new MojoExecutionException( e.getMessage(), e );
473         }
474         catch ( IOException e )
475         {
476             throw new MojoExecutionException( e.getMessage(), e );
477         }
478         catch ( ArchiveException e )
479         {
480             throw new MojoExecutionException( e.getMessage(), e );
481         }
482         catch ( ArtifactNotFoundException e )
483         {
484             throw new MojoExecutionException( e.getMessage(), e );
485         }
486         catch ( ArtifactResolutionException e )
487         {
488             throw new MojoExecutionException( e.getMessage(), e );
489         }
490         finally
491         {
492             IOUtils.closeQuietly( os );
493             IOUtils.closeQuietly( tmpManifestWriter );
494             IOUtils.closeQuietly( execWarJarOutputStream );
495             IOUtils.closeQuietly( tmpPropertiesFileOutputStream );
496         }
497     }
498 
499     private 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     private 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             Enumeration<JarEntry> jarEntries = jarFile.entries();
536             while ( jarEntries.hasMoreElements() )
537             {
538                 JarEntry jarEntry = jarEntries.nextElement();
539                 os.putArchiveEntry( new JarArchiveEntry( jarEntry.getName() ) );
540                 IOUtils.copy( jarFile.getInputStream( jarEntry ), os );
541                 os.closeArchiveEntry();
542             }
543             os.flush();
544         }
545         finally
546         {
547             IOUtils.closeQuietly( os );
548             IOUtils.closeQuietly( warOutputStream );
549         }
550         return tmpWar;
551     }
552 }