View Javadoc

1   package org.apache.tomcat.maven.plugin.tomcat6;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  
23  import org.apache.catalina.Context;
24  import org.apache.catalina.Engine;
25  import org.apache.catalina.Host;
26  import org.apache.catalina.LifecycleException;
27  import org.apache.catalina.Wrapper;
28  import org.apache.catalina.connector.Connector;
29  import org.apache.catalina.loader.WebappLoader;
30  import org.apache.catalina.realm.MemoryRealm;
31  import org.apache.catalina.servlets.DefaultServlet;
32  import org.apache.catalina.startup.Catalina;
33  import org.apache.catalina.startup.Embedded;
34  import org.apache.maven.artifact.Artifact;
35  import org.apache.maven.artifact.factory.ArtifactFactory;
36  import org.apache.maven.artifact.repository.ArtifactRepository;
37  import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
38  import org.apache.maven.artifact.resolver.ArtifactResolutionException;
39  import org.apache.maven.artifact.resolver.ArtifactResolver;
40  import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
41  import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
42  import org.apache.maven.artifact.versioning.VersionRange;
43  import org.apache.maven.execution.MavenSession;
44  import org.apache.maven.plugin.MojoExecutionException;
45  import org.apache.maven.plugin.MojoFailureException;
46  import org.apache.maven.plugins.annotations.Component;
47  import org.apache.maven.plugins.annotations.Parameter;
48  import org.apache.maven.project.MavenProject;
49  import org.apache.maven.shared.filtering.MavenFileFilter;
50  import org.apache.maven.shared.filtering.MavenFileFilterRequest;
51  import org.apache.maven.shared.filtering.MavenFilteringException;
52  import org.apache.tomcat.maven.common.config.AbstractWebapp;
53  import org.apache.tomcat.maven.common.run.EmbeddedRegistry;
54  import org.codehaus.plexus.archiver.ArchiverException;
55  import org.codehaus.plexus.archiver.UnArchiver;
56  import org.codehaus.plexus.archiver.manager.ArchiverManager;
57  import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
58  import org.codehaus.plexus.classworlds.ClassWorld;
59  import org.codehaus.plexus.classworlds.realm.ClassRealm;
60  import org.codehaus.plexus.classworlds.realm.DuplicateRealmException;
61  import org.codehaus.plexus.util.DirectoryScanner;
62  import org.codehaus.plexus.util.FileUtils;
63  import org.codehaus.plexus.util.StringUtils;
64  import org.w3c.dom.Document;
65  import org.w3c.dom.NamedNodeMap;
66  import org.w3c.dom.Node;
67  import org.xml.sax.SAXException;
68  
69  import javax.xml.parsers.DocumentBuilder;
70  import javax.xml.parsers.DocumentBuilderFactory;
71  import javax.xml.parsers.ParserConfigurationException;
72  import java.io.File;
73  import java.io.FileNotFoundException;
74  import java.io.IOException;
75  import java.net.InetAddress;
76  import java.net.MalformedURLException;
77  import java.net.URL;
78  import java.util.ArrayList;
79  import java.util.Collection;
80  import java.util.Collections;
81  import java.util.List;
82  import java.util.Map;
83  import java.util.Set;
84  
85  /**
86   * Abstract goal that provides common configuration for embedded Tomcat goals.
87   *
88   * @author Jurgen Lust
89   * @author Mark Hobson <markhobson@gmail.com>
90   */
91  public abstract class AbstractRunMojo
92      extends AbstractI18NTomcat6Mojo
93  {
94      // ---------------------------------------------------------------------
95      // Mojo Components
96      // ---------------------------------------------------------------------
97  
98      /**
99       * Used to look up Artifacts in the remote repository.
100      */
101     @Component(role = ArtifactFactory.class)
102     protected ArtifactFactory artifactFactory;
103 
104     /**
105      * Location of the local repository.
106      */
107     @Parameter(defaultValue = "${localRepository}", required = true, readonly = true)
108     private ArtifactRepository artifactRepository;
109 
110     /**
111      * Used to look up Artifacts in the remote repository.
112      */
113     @Component(role = ArtifactResolver.class)
114     protected ArtifactResolver artifactResolver;
115 
116     // ----------------------------------------------------------------------
117     // Mojo Parameters
118     // ----------------------------------------------------------------------
119 
120     /**
121      * The packaging of the Maven project that this goal operates upon.
122      */
123     @Parameter(defaultValue = "${project.packaging}", required = true, readonly = true)
124     private String packaging;
125 
126     /**
127      * The directory to create the Tomcat server configuration under.
128      */
129     @Parameter(defaultValue = "${project.build.directory}/tomcat")
130     private File configurationDir;
131 
132     /**
133      * The port to run the Tomcat server on.
134      */
135     @Parameter(property = "maven.tomcat.port", defaultValue = "8080")
136     private int port;
137 
138     /**
139      * this IP address will be used on all ports.
140      *
141      * @since 2.2
142      */
143     @Parameter(property = "maven.tomcat.address")
144     private String address;
145 
146     /**
147      * The AJP port to run the Tomcat server on.
148      * By default it's 0 this means won't be started.
149      * The ajp connector will be started only for value > 0.
150      *
151      * @since 2.0
152      */
153     @Parameter(property = "maven.tomcat.ajp.port", defaultValue = "0")
154     private int ajpPort;
155 
156     /**
157      * The AJP protocol to run the Tomcat server on.
158      * By default it's ajp.
159      * NOTE The ajp connector will be started only if {@link #ajpPort} > 0.
160      *
161      * @since 2.0
162      */
163     @Parameter(property = "maven.tomcat.ajp.protocol", defaultValue = "ajp")
164     private String ajpProtocol;
165 
166     /**
167      * The https port to run the Tomcat server on.
168      * By default it's 0 this means won't be started.
169      * The https connector will be started only for value > 0.
170      *
171      * @since 1.0
172      */
173     @Parameter(property = "maven.tomcat.httpsPort", defaultValue = "0")
174     private int httpsPort;
175 
176     /**
177      * The max post size to run the Tomcat server on.
178      * By default it's 2097152 bytes. That's the default Tomcat configuration.
179      * Set this value to 0 or less to disable the post size limit.
180      *
181      * @since 2.3
182      */
183     @Parameter(property = "maven.tomcat.maxPostSize", defaultValue = "2097152")
184     private int maxPostSize;
185 
186     /**
187      * The character encoding to use for decoding URIs.
188      *
189      * @since 1.0
190      */
191     @Parameter(property = "maven.tomcat.uriEncoding", defaultValue = "ISO-8859-1")
192     private String uriEncoding;
193 
194     /**
195      * List of System properties to pass to the Tomcat Server.
196      *
197      * @since 1.0-alpha-2
198      */
199     @Parameter
200     private Map<String, String> systemProperties;
201 
202     /**
203      * The directory contains additional configuration Files that copied in the Tomcat conf Directory.
204      *
205      * @since 1.0-alpha-2
206      */
207     @Parameter(property = "maven.tomcat.additionalConfigFilesDir", defaultValue = "${basedir}/src/main/tomcatconf")
208     private File additionalConfigFilesDir;
209 
210     /**
211      * server.xml to use <b>Note if you use this you must configure in this file your webapp paths</b>.
212      *
213      * @since 1.0-alpha-2
214      */
215     @Parameter(property = "maven.tomcat.serverXml")
216     private File serverXml;
217 
218     /**
219      * overriding the providing web.xml to run tomcat
220      * <b>This override the global Tomcat web.xml located in $CATALINA_HOME/conf/</b>
221      *
222      * @since 1.0-alpha-2
223      */
224     @Parameter(property = "maven.tomcat.webXml")
225     private File tomcatWebXml;
226 
227     /**
228      * Set this to true to allow Maven to continue to execute after invoking
229      * the run goal.
230      *
231      * @since 1.0
232      */
233     @Parameter(property = "maven.tomcat.fork", defaultValue = "false")
234     private boolean fork;
235 
236     /**
237      * Will create a tomcat context for each dependencies of war type with 'scope' set to 'tomcat'.
238      * In other words, dependencies with:
239      * <pre>
240      *    &lt;type&gt;war&lt;/type&gt;
241      *    &lt;scope&gt;tomcat&lt;/scope&gt;
242      * </pre>
243      * To preserve backward compatibility it's false by default.
244      *
245      * @since 1.0
246      * @deprecated use webapps instead
247      */
248     @Parameter(property = "maven.tomcat.addContextWarDependencies", defaultValue = "false")
249     private boolean addContextWarDependencies;
250 
251     /**
252      * The maven project.
253      *
254      * @since 1.0
255      */
256     @Component
257     protected MavenProject project;
258 
259     /**
260      * The archive manager.
261      *
262      * @since 1.0
263      */
264     @Component(role = ArchiverManager.class)
265     private ArchiverManager archiverManager;
266 
267     /**
268      * if <code>true</code> a new classLoader separated from maven core will be created to start tomcat.
269      *
270      * @since 1.0
271      */
272     @Parameter(property = "tomcat.useSeparateTomcatClassLoader", defaultValue = "false")
273     protected boolean useSeparateTomcatClassLoader;
274 
275     /**
276      * @since 1.0
277      */
278     @Parameter(defaultValue = "${plugin.artifacts}", required = true)
279     private List<Artifact> pluginArtifacts;
280 
281     /**
282      * If set to true ignore if packaging of project is not 'war'.
283      *
284      * @since 1.0
285      */
286     @Parameter(property = "tomcat.ignorePackaging", defaultValue = "false")
287     private boolean ignorePackaging;
288 
289     /**
290      * Override the default keystoreFile for the HTTPS connector (if enabled)
291      *
292      * @since 1.1
293      */
294     @Parameter
295     private String keystoreFile;
296 
297     /**
298      * Override the default keystorePass for the HTTPS connector (if enabled)
299      *
300      * @since 1.1
301      */
302     @Parameter
303     private String keystorePass;
304 
305     /**
306      * Override the type of keystore file to be used for the server certificate. If not specified, the default value is "JKS".
307      *
308      * @since 2.0
309      */
310     @Parameter(defaultValue = "JKS")
311     private String keystoreType;
312 
313     /**
314      * Override the default truststoreFile for the HTTPS connector (if enabled)
315      *
316      * @since 2.2
317      */
318     @Parameter
319     private String truststoreFile;
320 
321     /**
322      * Override the default truststorePass for the HTTPS connector (if enabled)
323      *
324      * @since 2.2
325      */
326     @Parameter
327     private String truststorePass;
328 
329     /**
330      * Override the default truststoreType for the HTTPS connector (if enabled)
331      *
332      * @since 2.2
333      */
334     @Parameter
335     private String truststoreType;
336 
337     /**
338      * Override the default truststoreProvider for the HTTPS connector (if enabled)
339      *
340      * @since 2.2
341      */
342     @Parameter
343     private String truststoreProvider;
344 
345     /**
346      * <p>
347      * Enables or disables naming support for the embedded Tomcat server. By default the embedded Tomcat
348      * in Tomcat 6 comes with naming enabled. In contrast to this the embedded Tomcat 7 comes with
349      * naming disabled by default.
350      * </p>
351      * <p>
352      * <strong>Note:</strong> This setting is ignored if you provide a <code>server.xml</code> for your
353      * Tomcat. Instead please configure naming in the <code>server.xml</code>.
354      * </p>
355      *
356      * @see <a href="http://tomcat.apache.org/tomcat-6.0-doc/api/org/apache/catalina/startup/Embedded.html">org.apache.catalina.startup.Embedded</a>
357      * @since 2.0
358      */
359     @Parameter(property = "maven.tomcat.useNaming", defaultValue = "true")
360     private boolean useNaming;
361 
362     /**
363      * Force context scanning if you don't use a context file with reloadable = "true".
364      * The other way to use contextReloadable is to add attribute reloadable = "true"
365      * in your context file.
366      *
367      * @since 2.0
368      */
369     @Parameter(property = "maven.tomcat.contextReloadable", defaultValue = "false")
370     protected boolean contextReloadable;
371 
372     /**
373      * represents the delay in seconds between each classPathScanning change invocation
374      *
375      * @see <a href="http://tomcat.apache.org/tomcat-6.0-doc/config/context.html">http://tomcat.apache.org/tomcat-6.0-doc/config/context.html</a>
376      */
377     @Parameter(property = "maven.tomcat.backgroundProcessorDelay", defaultValue = "-1")
378     protected int backgroundProcessorDelay = -1;
379 
380     /**
381      * The path of the Tomcat context XML file.
382      */
383     @Parameter(defaultValue = "src/main/webapp/META-INF/context.xml")
384     protected File contextFile;
385 
386     /**
387      * The protocol to run the Tomcat server on.
388      * By default it's HTTP/1.1.
389      * See possible values <a href="http://tomcat.apache.org/tomcat-6.0-doc/config/http.html">HTTP Connector</a>
390      * protocol attribute
391      *
392      * @since 2.0
393      */
394     @Parameter(property = "maven.tomcat.protocol", defaultValue = "HTTP/1.1")
395     private String protocol;
396 
397     /**
398      * The path of the Tomcat users XML file.
399      */
400     @Parameter(property = "maven.tomcat.tomcatUsers.file")
401     private File tomcatUsers;
402 
403     /**
404      * to install a manager in your embeded tomcat
405      *
406      * @since 2.0
407      */
408     @Parameter
409     private File managerWarPath;
410 
411 
412     /**
413      * Skip execution
414      *
415      * @since 2.0
416      */
417     @Parameter(property = "maven.tomcat.skip", defaultValue = "false")
418     protected boolean skip;
419 
420     /**
421      * @see {@link Webapp}
422      * @since 2.0
423      */
424     @Parameter
425     private List<Webapp> webapps;
426 
427     /**
428      * configure host name
429      *
430      * @since 2.1
431      */
432     @Parameter(property = "maven.tomcat.hostName", defaultValue = "localhost")
433     protected String hostName;
434 
435     /**
436      * configure aliases
437      * see <a href="http://tomcat.apache.org/tomcat-6.0-doc/config/host.html#Host_Name_Aliases">Host Name aliases</a>
438      *
439      * @since 2.1
440      */
441     @Parameter
442     protected String[] aliases;
443 
444     // ----------------------------------------------------------------------
445     // Fields
446     // ----------------------------------------------------------------------
447 
448     /**
449      * @since 1.0
450      */
451     private ClassRealm tomcatRealm;
452 
453     /**
454      * The static context
455      *
456      * @since 2.0
457      */
458     @Parameter(property = "maven.tomcat.staticContextPath", defaultValue = "/")
459     private String staticContextPath;
460 
461     /**
462      * The static context docroot base fully qualified path.
463      * if <code>null</code> static context won't be added
464      *
465      * @since 2.0
466      */
467     @Parameter(property = "maven.tomcat.staticContextDocbase")
468     private String staticContextDocbase;
469 
470     /**
471      * Class loader class to set.
472      *
473      * @since 2.0
474      */
475     @Parameter
476     protected String classLoaderClass;
477 
478     /**
479      * @since 2.2
480      */
481     @Parameter(property = "maven.tomcat.useBodyEncodingForURI", defaultValue = "false")
482     protected boolean useBodyEncodingForURI;
483 
484     @Parameter(defaultValue = "${session}", readonly = true, required = true)
485     protected MavenSession session;
486 
487     @Component(role = MavenFileFilter.class, hint = "default")
488     protected MavenFileFilter mavenFileFilter;
489 
490     // ----------------------------------------------------------------------
491     // Mojo Implementation
492     // ----------------------------------------------------------------------
493 
494     /**
495      * {@inheritDoc}
496      */
497     public void execute()
498         throws MojoExecutionException, MojoFailureException
499     {
500         if ( skip )
501         {
502             getLog().info( "skip execution" );
503             return;
504         }
505         // ensure project is a web application and we have at least additionnal webapps to run
506         if ( !isWar() && !addContextWarDependencies && getAdditionalWebapps().isEmpty() )
507         {
508             getLog().info( messagesProvider.getMessage( "AbstractRunMojo.nonWar" ) );
509             return;
510         }
511         ClassLoader originalClassLoader = null;
512         try
513         {
514 
515             if ( useSeparateTomcatClassLoader )
516             {
517                 originalClassLoader = Thread.currentThread().getContextClassLoader();
518             }
519             getLog().info( messagesProvider.getMessage( "AbstractRunMojo.runningWar", getWebappUrl() ) );
520 
521             initConfiguration();
522             startContainer();
523             if ( !fork )
524             {
525                 waitIndefinitely();
526             }
527         }
528         catch ( LifecycleException exception )
529         {
530             throw new MojoExecutionException( messagesProvider.getMessage( "AbstractRunMojo.cannotStart" ), exception );
531         }
532         catch ( IOException exception )
533         {
534             throw new MojoExecutionException(
535                 messagesProvider.getMessage( "AbstractRunMojo.cannotCreateConfiguration" ), exception );
536         }
537         catch ( MavenFilteringException e )
538         {
539             throw new MojoExecutionException( "filtering issue: " + e.getMessage(), e );
540         }
541         finally
542         {
543             if ( useSeparateTomcatClassLoader )
544             {
545                 Thread.currentThread().setContextClassLoader( originalClassLoader );
546             }
547         }
548     }
549 
550     // ----------------------------------------------------------------------
551     // Protected Methods
552     // ----------------------------------------------------------------------
553 
554     /**
555      * Gets the webapp context path to use for the web application being run.
556      *
557      * @return the webapp context path
558      */
559     protected String getPath()
560     {
561         return path;
562     }
563 
564     /**
565      * Gets the context to run this web application under for the specified embedded Tomcat.
566      *
567      * @param container the embedded Tomcat container being used
568      * @return the context to run this web application under
569      * @throws IOException            if the context could not be created
570      * @throws MojoExecutionException in case of an error creating the context
571      */
572     protected Context createContext( Embedded container )
573         throws IOException, MojoExecutionException
574     {
575         String contextPath = getPath();
576         Context context =
577             container.createContext( "/".equals( contextPath ) ? "" : contextPath, getDocBase().getAbsolutePath() );
578 
579         if ( useSeparateTomcatClassLoader )
580         {
581             context.setParentClassLoader( getTomcatClassLoader() );
582         }
583 
584         final WebappLoader webappLoader = createWebappLoader();
585 
586         if ( classLoaderClass != null )
587         {
588             webappLoader.setLoaderClass( classLoaderClass );
589         }
590 
591         context.setLoader( webappLoader );
592         File contextFile = getContextFile();
593         if ( contextFile != null )
594         {
595             context.setConfigFile( getContextFile().getAbsolutePath() );
596         }
597         return context;
598     }
599 
600     /**
601      * Gets the webapp loader to run this web application under.
602      *
603      * @return the webapp loader to use
604      * @throws IOException            if the webapp loader could not be created
605      * @throws MojoExecutionException in case of an error creating the webapp loader
606      */
607     protected WebappLoader createWebappLoader()
608         throws IOException, MojoExecutionException
609     {
610         if ( useSeparateTomcatClassLoader )
611         {
612             return ( isContextReloadable() )
613                 ? new ExternalRepositoriesReloadableWebappLoader( getTomcatClassLoader(), getLog() )
614                 : new WebappLoader( getTomcatClassLoader() );
615         }
616 
617         return ( isContextReloadable() )
618             ? new ExternalRepositoriesReloadableWebappLoader( Thread.currentThread().getContextClassLoader(), getLog() )
619             : new WebappLoader( Thread.currentThread().getContextClassLoader() );
620     }
621 
622     /**
623      * Determine whether the passed context.xml file declares the context as reloadable or not.
624      *
625      * @return false by default, true if  reloadable="true" in context.xml.
626      */
627     protected boolean isContextReloadable()
628         throws MojoExecutionException
629     {
630         if ( contextReloadable || backgroundProcessorDelay > 0 )
631         {
632             return true;
633         }
634         // determine whether to use a reloadable Loader or not (default is false).
635         boolean reloadable = false;
636         try
637         {
638             if ( contextFile != null && contextFile.exists() )
639             {
640                 DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
641                 DocumentBuilder builder = builderFactory.newDocumentBuilder();
642                 Document contextDoc = builder.parse( contextFile );
643                 contextDoc.getDocumentElement().normalize();
644 
645                 NamedNodeMap nodeMap = contextDoc.getDocumentElement().getAttributes();
646                 Node reloadableAttribute = nodeMap.getNamedItem( "reloadable" );
647 
648                 reloadable =
649                     ( reloadableAttribute != null ) ? Boolean.valueOf( reloadableAttribute.getNodeValue() ) : false;
650             }
651             getLog().debug( "context reloadable: " + reloadable );
652         }
653         catch ( IOException ioe )
654         {
655             getLog().error( "Could not parse file: [" + contextFile.getAbsolutePath() + "]", ioe );
656         }
657         catch ( ParserConfigurationException pce )
658         {
659             getLog().error( "Could not configure XML parser", pce );
660         }
661         catch ( SAXException se )
662         {
663             getLog().error( "Could not parse file: [" + contextFile.getAbsolutePath() + "]", se );
664         }
665 
666         return reloadable;
667     }
668 
669 
670     /**
671      * Gets the webapp directory to run.
672      *
673      * @return the webapp directory
674      */
675     protected abstract File getDocBase();
676 
677     /**
678      * Gets the Tomcat context XML file to use.
679      *
680      * @return the context XML file
681      */
682     protected abstract File getContextFile()
683         throws MojoExecutionException;
684 
685     // ----------------------------------------------------------------------
686     // Private Methods
687     // ----------------------------------------------------------------------
688 
689     /**
690      * Gets whether this project uses WAR packaging.
691      *
692      * @return whether this project uses WAR packaging
693      */
694     protected boolean isWar()
695     {
696         return "war".equals( packaging ) || ignorePackaging;
697     }
698 
699     /**
700      * Gets the URL of the running webapp.
701      *
702      * @return the URL of the running webapp
703      * @throws MalformedURLException if the running webapp URL is invalid
704      */
705     private URL getWebappUrl()
706         throws MalformedURLException
707     {
708         return new URL( "http", "localhost", port, getPath() );
709     }
710 
711     /**
712      * Creates the Tomcat configuration directory with the necessary resources.
713      *
714      * @throws IOException            if the Tomcat configuration could not be created
715      * @throws MojoExecutionException if the Tomcat configuration could not be created
716      */
717     private void initConfiguration()
718         throws IOException, MojoExecutionException, MavenFilteringException
719     {
720         if ( configurationDir.exists() )
721         {
722             getLog().info( messagesProvider.getMessage( "AbstractRunMojo.usingConfiguration", configurationDir ) );
723         }
724         else
725         {
726             getLog().info( messagesProvider.getMessage( "AbstractRunMojo.creatingConfiguration", configurationDir ) );
727 
728             configurationDir.mkdirs();
729 
730             File confDir = new File( configurationDir, "conf" );
731             confDir.mkdir();
732 
733             copyFile( "/conf/tomcat-users.xml", new File( confDir, "tomcat-users.xml" ) );
734 
735             if ( tomcatWebXml != null )
736             {
737                 if ( !tomcatWebXml.exists() )
738                 {
739                     throw new MojoExecutionException( " tomcatWebXml " + tomcatWebXml.getPath() + " not exists" );
740                 }
741                 //MTOMCAT-42  here it's a real file resources not a one coming with the mojo 
742                 FileUtils.copyFile( tomcatWebXml, new File( confDir, "web.xml" ) );
743                 //MTOMCAT-128 apply filtering
744                 MavenFileFilterRequest mavenFileFilterRequest = new MavenFileFilterRequest();
745                 mavenFileFilterRequest.setFrom( tomcatWebXml );
746                 mavenFileFilterRequest.setTo( new File( confDir, "web.xml" ) );
747                 mavenFileFilterRequest.setMavenProject( project );
748                 mavenFileFilterRequest.setMavenSession( session );
749                 mavenFileFilterRequest.setFiltering( true );
750 
751                 mavenFileFilter.copyFile( mavenFileFilterRequest );
752             }
753             else
754             {
755                 copyFile( "/conf/web.xml", new File( confDir, "web.xml" ) );
756             }
757 
758             File logDir = new File( configurationDir, "logs" );
759             logDir.mkdir();
760 
761             File webappsDir = new File( configurationDir, "webapps" );
762             webappsDir.mkdir();
763             if ( managerWarPath != null && managerWarPath.exists() )
764             {
765                 FileUtils.copyFileToDirectory( managerWarPath, webappsDir );
766             }
767 
768             if ( additionalConfigFilesDir != null && additionalConfigFilesDir.exists() )
769             {
770                 DirectoryScanner scanner = new DirectoryScanner();
771                 scanner.addDefaultExcludes();
772                 scanner.setBasedir( additionalConfigFilesDir.getPath() );
773                 scanner.scan();
774 
775                 String[] files = scanner.getIncludedFiles();
776 
777                 if ( files != null && files.length > 0 )
778                 {
779                     getLog().info( "Coping additional tomcat config files" );
780 
781                     for ( int i = 0; i < files.length; i++ )
782                     {
783                         File file = new File( additionalConfigFilesDir, files[i] );
784 
785                         getLog().info( " copy " + file.getName() );
786 
787                         FileUtils.copyFileToDirectory( file, confDir );
788                     }
789                 }
790             }
791         }
792     }
793 
794     /**
795      * Copies the specified class resource to the specified file.
796      *
797      * @param fromPath the path of the class resource to copy
798      * @param toFile   the file to copy to
799      * @throws IOException if the file could not be copied
800      */
801     private void copyFile( String fromPath, File toFile )
802         throws IOException
803     {
804         URL fromURL = getClass().getResource( fromPath );
805 
806         if ( fromURL == null )
807         {
808             throw new FileNotFoundException( fromPath );
809         }
810 
811         FileUtils.copyURLToFile( fromURL, toFile );
812     }
813 
814     /**
815      * Starts the embedded Tomcat server.
816      *
817      * @throws IOException            if the server could not be configured
818      * @throws LifecycleException     if the server could not be started
819      * @throws MojoExecutionException if the server could not be configured
820      */
821     private void startContainer()
822         throws IOException, LifecycleException, MojoExecutionException
823     {
824         String previousCatalinaBase = System.getProperty( "catalina.base" );
825 
826         try
827         {
828 
829             // Set the system properties
830             setupSystemProperties();
831 
832             System.setProperty( "catalina.base", configurationDir.getAbsolutePath() );
833             System.setProperty( "catalina.home", configurationDir.getAbsolutePath() );
834 
835             File catalinaPolicy = new File( configurationDir, "conf/catalina.policy" );
836 
837             if ( catalinaPolicy.exists() )
838             {
839                 // FIXME restore previous value ?
840                 System.setProperty( "java.security.policy", catalinaPolicy.getAbsolutePath() );
841             }
842 
843             final Embedded container;
844             if ( serverXml != null )
845             {
846                 if ( !serverXml.exists() )
847                 {
848                     throw new MojoExecutionException( serverXml.getPath() + " not exists" );
849                 }
850 
851                 container = new Catalina();
852                 container.setCatalinaHome( configurationDir.getAbsolutePath() );
853                 container.setCatalinaBase( configurationDir.getAbsolutePath() );
854                 ( (Catalina) container ).setConfigFile( serverXml.getPath() );
855                 ( (Catalina) container ).setRedirectStreams( true );
856                 ( (Catalina) container ).setUseNaming( this.useNaming );
857 
858                 container.start();
859             }
860             else
861             {
862                 // create server
863                 container = new Embedded();
864                 container.setCatalinaHome( configurationDir.getAbsolutePath() );
865                 MemoryRealm memoryRealm = new MemoryRealm();
866 
867                 if ( tomcatUsers != null )
868                 {
869                     if ( !tomcatUsers.exists() )
870                     {
871                         throw new MojoExecutionException( " tomcatUsers " + tomcatUsers.getPath() + " not exists" );
872                     }
873                     getLog().info( "use tomcat-users.xml from " + tomcatUsers.getAbsolutePath() );
874                     memoryRealm.setPathname( tomcatUsers.getAbsolutePath() );
875 
876                 }
877 
878                 container.setRealm( memoryRealm );
879                 container.setUseNaming( useNaming );
880 
881                 //container.createLoader( getTomcatClassLoader() ).
882 
883                 // create context
884                 Context context = createContext( container );
885 
886                 // create host
887                 String appBase = new File( configurationDir, "webapps" ).getAbsolutePath();
888                 Host host = container.createHost( "localHost", appBase );
889 
890                 if ( hostName != null )
891                 {
892                     host.setName( hostName );
893                 }
894                 if ( aliases != null )
895                 {
896                     for ( String alias : aliases )
897                     {
898                         host.addAlias( alias );
899                     }
900                 }
901 
902                 host.addChild( context );
903                 createStaticContext( container, context, host );
904                 if ( addContextWarDependencies || !getAdditionalWebapps().isEmpty() )
905                 {
906                     Collection<Context> dependencyContexts = createDependencyContexts( container );
907                     for ( Context extraContext : dependencyContexts )
908                     {
909                         host.addChild( extraContext );
910                     }
911                 }
912 
913                 // create engine
914                 Engine engine = container.createEngine();
915                 engine.setName( "localEngine-" + port );
916                 engine.addChild( host );
917                 engine.setDefaultHost( host.getName() );
918                 container.addEngine( engine );
919 
920                 getLog().debug( "start tomcat instance on http port:" + port + " and protocol: " + protocol );
921 
922                 // create http connector
923                 Connector httpConnector = container.createConnector( (InetAddress) null, port, protocol );
924                 httpConnector.setMaxPostSize( maxPostSize );
925                 if ( httpsPort > 0 )
926                 {
927                     httpConnector.setRedirectPort( httpsPort );
928                 }
929                 httpConnector.setURIEncoding( uriEncoding );
930                 httpConnector.setUseBodyEncodingForURI( this.useBodyEncodingForURI );
931 
932                 if ( address != null )
933                 {
934                     httpConnector.setAttribute( "address", address );
935                 }
936 
937                 container.addConnector( httpConnector );
938 
939                 // create https connector
940                 if ( httpsPort > 0 )
941                 {
942                     Connector httpsConnector = container.createConnector( (InetAddress) null, httpsPort, true );
943                     httpsConnector.setSecure( true );
944                     httpsConnector.setMaxPostSize( maxPostSize );
945                     httpsConnector.setProperty( "SSLEnabled", "true" );
946                     // should be default but configure it anyway
947                     httpsConnector.setProperty( "sslProtocol", "TLS" );
948                     if ( keystoreFile != null )
949                     {
950                         httpsConnector.setAttribute( "keystoreFile", keystoreFile );
951                     }
952                     if ( keystorePass != null )
953                     {
954                         httpsConnector.setAttribute( "keystorePass", keystorePass );
955                     }
956                     if ( keystoreType != null )
957                     {
958                         httpsConnector.setAttribute( "keystoreType", keystoreType );
959                     }
960 
961                     if ( truststoreFile != null )
962                     {
963                         httpsConnector.setAttribute( "truststoreFile", truststoreFile );
964                     }
965 
966                     if ( truststorePass != null )
967                     {
968                         httpsConnector.setAttribute( "truststorePass", truststorePass );
969                     }
970 
971                     if ( truststoreType != null )
972                     {
973                         httpsConnector.setAttribute( "truststoreType", truststoreType );
974                     }
975 
976                     if ( truststoreProvider != null )
977                     {
978                         httpsConnector.setAttribute( "truststoreProvider", truststoreProvider );
979                     }
980 
981                     httpsConnector.setUseBodyEncodingForURI( this.useBodyEncodingForURI );
982 
983                     if ( address != null )
984                     {
985                         httpsConnector.setAttribute( "address", address );
986                     }
987 
988                     container.addConnector( httpsConnector );
989 
990                 }
991 
992                 // create ajp connector
993                 if ( ajpPort > 0 )
994                 {
995                     Connector ajpConnector = container.createConnector( (InetAddress) null, ajpPort, ajpProtocol );
996                     ajpConnector.setURIEncoding( uriEncoding );
997                     ajpConnector.setUseBodyEncodingForURI( this.useBodyEncodingForURI );
998                     if ( address != null )
999                     {
1000                         ajpConnector.setAttribute( "address", address );
1001                     }
1002                     container.addConnector( ajpConnector );
1003                 }
1004                 if ( useSeparateTomcatClassLoader )
1005                 {
1006                     Thread.currentThread().setContextClassLoader( getTomcatClassLoader() );
1007                     engine.setParentClassLoader( getTomcatClassLoader() );
1008                 }
1009                 container.start();
1010             }
1011 
1012             EmbeddedRegistry.getInstance().register( container );
1013         }
1014         finally
1015         {
1016             if ( previousCatalinaBase != null )
1017             {
1018                 System.setProperty( "catalina.base", previousCatalinaBase );
1019             }
1020         }
1021     }
1022 
1023     private List<Webapp> getAdditionalWebapps()
1024     {
1025         if ( webapps == null )
1026         {
1027             return Collections.emptyList();
1028         }
1029         return webapps;
1030     }
1031 
1032     protected ClassRealm getTomcatClassLoader()
1033         throws MojoExecutionException
1034     {
1035         if ( this.tomcatRealm != null )
1036         {
1037             return tomcatRealm;
1038         }
1039         try
1040         {
1041             ClassWorld world = new ClassWorld();
1042             ClassRealm root = world.newRealm( "tomcat", Thread.currentThread().getContextClassLoader() );
1043 
1044             for ( Artifact pluginArtifact : pluginArtifacts )
1045             {
1046                 // add all plugin artifacts see https://issues.apache.org/jira/browse/MTOMCAT-122
1047                 if ( pluginArtifact.getFile() != null )
1048                 {
1049                     root.addURL( pluginArtifact.getFile().toURI().toURL() );
1050                 }
1051 
1052             }
1053             tomcatRealm = root;
1054             return root;
1055         }
1056         catch ( DuplicateRealmException e )
1057         {
1058             throw new MojoExecutionException( e.getMessage(), e );
1059         }
1060         catch ( MalformedURLException e )
1061         {
1062             throw new MojoExecutionException( e.getMessage(), e );
1063         }
1064     }
1065 
1066     @SuppressWarnings("unchecked")
1067     public Set<Artifact> getProjectArtifacts()
1068     {
1069         return project.getArtifacts();
1070     }
1071 
1072     /**
1073      * Causes the current thread to wait indefinitely. This method does not return.
1074      */
1075     private void waitIndefinitely()
1076     {
1077         Object lock = new Object();
1078 
1079         synchronized ( lock )
1080         {
1081             try
1082             {
1083                 lock.wait();
1084             }
1085             catch ( InterruptedException exception )
1086             {
1087                 getLog().warn( messagesProvider.getMessage( "AbstractRunMojo.interrupted" ), exception );
1088             }
1089         }
1090     }
1091 
1092 
1093     /**
1094      * Set the SystemProperties from the configuration.
1095      */
1096     private void setupSystemProperties()
1097     {
1098         if ( systemProperties != null && !systemProperties.isEmpty() )
1099         {
1100             getLog().info( "setting SystemProperties:" );
1101 
1102             for ( String key : systemProperties.keySet() )
1103             {
1104                 String value = systemProperties.get( key );
1105 
1106                 if ( value != null )
1107                 {
1108                     getLog().info( " " + key + "=" + value );
1109                     System.setProperty( key, value );
1110                 }
1111                 else
1112                 {
1113                     getLog().info( "skip sysProps " + key + " with empty value" );
1114                 }
1115             }
1116         }
1117     }
1118 
1119 
1120     /**
1121      * Allows the startup of additional webapps in the tomcat container by declaration with scope
1122      * "tomcat".
1123      *
1124      * @param container tomcat
1125      * @return dependency tomcat contexts of warfiles in scope "tomcat" and those from webapps
1126      */
1127     private Collection<Context> createDependencyContexts( Embedded container )
1128         throws MojoExecutionException
1129     {
1130         getLog().info( "Deploying dependency wars" );
1131         // Let's add other modules
1132         List<Context> contexts = new ArrayList<Context>();
1133 
1134         ScopeArtifactFilter filter = new ScopeArtifactFilter( "tomcat" );
1135         @SuppressWarnings("unchecked") Set<Artifact> artifacts = project.getArtifacts();
1136         for ( Artifact artifact : artifacts )
1137         {
1138             // Artifact is not yet registered and it has neither test, nor a
1139             // provided scope, not is it optional
1140             if ( "war".equals( artifact.getType() ) && !artifact.isOptional() && filter.include( artifact ) )
1141             {
1142                 addContextFromArtifact( container, contexts, artifact, "/" + artifact.getArtifactId(), null );
1143             }
1144         }
1145 
1146         for ( AbstractWebapp additionalWebapp : getAdditionalWebapps() )
1147         {
1148             String contextPath = additionalWebapp.getContextPath();
1149             if ( !contextPath.startsWith( "/" ) )
1150             {
1151                 contextPath = "/" + contextPath;
1152             }
1153             addContextFromArtifact( container, contexts, getArtifact( additionalWebapp ), contextPath,
1154                                     additionalWebapp.getContextFile() );
1155         }
1156         return contexts;
1157     }
1158 
1159 
1160     private void addContextFromArtifact( Embedded container, List<Context> contexts, Artifact artifact,
1161                                          String contextPath, File contextXml )
1162         throws MojoExecutionException
1163     {
1164         getLog().info( "Deploy warfile: " + String.valueOf( artifact.getFile() ) + " to contextPath: " + contextPath );
1165         File webapps = new File( configurationDir, "webapps" );
1166         File artifactWarDir = new File( webapps, artifact.getArtifactId() );
1167         if ( !artifactWarDir.exists() )
1168         {
1169             //dont extract if exists
1170             artifactWarDir.mkdir();
1171             try
1172             {
1173                 UnArchiver unArchiver = archiverManager.getUnArchiver( "zip" );
1174                 unArchiver.setSourceFile( artifact.getFile() );
1175                 unArchiver.setDestDirectory( artifactWarDir );
1176 
1177                 // Extract the module
1178                 unArchiver.extract();
1179             }
1180             catch ( NoSuchArchiverException e )
1181             {
1182                 getLog().error( e );
1183                 return;
1184             }
1185             catch ( ArchiverException e )
1186             {
1187                 getLog().error( e );
1188                 return;
1189             }
1190         }
1191         WebappLoader webappLoader = new WebappLoader( Thread.currentThread().getContextClassLoader() );
1192         Context context = container.createContext( contextPath, artifactWarDir.getAbsolutePath() );
1193         context.setLoader( webappLoader );
1194 
1195         File contextFile = contextXml != null ? contextXml : getContextFile();
1196         if ( contextFile != null )
1197         {
1198             context.setConfigFile( contextFile.getAbsolutePath() );
1199         }
1200         contexts.add( context );
1201     }
1202 
1203 
1204     private void createStaticContext( final Embedded container, Context context, Host host )
1205     {
1206         if ( staticContextDocbase != null )
1207         {
1208             Context staticContext = container.createContext( staticContextPath, staticContextDocbase );
1209             staticContext.setPrivileged( true );
1210             Wrapper servlet = context.createWrapper();
1211             servlet.setServletClass( DefaultServlet.class.getName() );
1212             servlet.setName( "staticContent" );
1213             staticContext.addChild( servlet );
1214             staticContext.addServletMapping( "/", "staticContent" );
1215             host.addChild( staticContext );
1216         }
1217     }
1218 
1219 
1220     /**
1221      * Resolves the Artifact from the remote repository if necessary. If no version is specified, it will be retrieved
1222      * from the dependency list or from the DependencyManagement section of the pom.
1223      *
1224      * @param additionalWebapp containing information about artifact from plugin configuration.
1225      * @return Artifact object representing the specified file.
1226      * @throws MojoExecutionException with a message if the version can't be found in DependencyManagement.
1227      */
1228     protected Artifact getArtifact( AbstractWebapp additionalWebapp )
1229         throws MojoExecutionException
1230     {
1231 
1232         Artifact artifact;
1233         VersionRange vr;
1234         try
1235         {
1236             vr = VersionRange.createFromVersionSpec( additionalWebapp.getVersion() );
1237         }
1238         catch ( InvalidVersionSpecificationException e )
1239         {
1240             getLog().warn( "fail to create versionRange from version: " + additionalWebapp.getVersion(), e );
1241             vr = VersionRange.createFromVersion( additionalWebapp.getVersion() );
1242         }
1243 
1244         if ( StringUtils.isEmpty( additionalWebapp.getClassifier() ) )
1245         {
1246             artifact = artifactFactory.createDependencyArtifact( additionalWebapp.getGroupId(),
1247                                                                  additionalWebapp.getArtifactId(), vr,
1248                                                                  additionalWebapp.getType(), null,
1249                                                                  Artifact.SCOPE_COMPILE );
1250         }
1251         else
1252         {
1253             artifact = artifactFactory.createDependencyArtifact( additionalWebapp.getGroupId(),
1254                                                                  additionalWebapp.getArtifactId(), vr,
1255                                                                  additionalWebapp.getType(),
1256                                                                  additionalWebapp.getClassifier(),
1257                                                                  Artifact.SCOPE_COMPILE );
1258         }
1259 
1260         try
1261         {
1262             artifactResolver.resolve( artifact, project.getRemoteArtifactRepositories(), this.artifactRepository );
1263         }
1264         catch ( ArtifactResolutionException e )
1265         {
1266             throw new MojoExecutionException( "Unable to resolve artifact.", e );
1267         }
1268         catch ( ArtifactNotFoundException e )
1269         {
1270             throw new MojoExecutionException( "Unable to find artifact.", e );
1271         }
1272 
1273         return artifact;
1274     }
1275 }