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 }