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 }