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.catalina.Context;
22  import org.apache.catalina.Host;
23  import org.apache.catalina.LifecycleException;
24  import org.apache.catalina.Wrapper;
25  import org.apache.catalina.connector.Connector;
26  import org.apache.catalina.core.StandardContext;
27  import org.apache.catalina.loader.WebappLoader;
28  import org.apache.catalina.realm.MemoryRealm;
29  import org.apache.catalina.servlets.DefaultServlet;
30  import org.apache.catalina.startup.Catalina;
31  import org.apache.catalina.startup.CatalinaProperties;
32  import org.apache.catalina.startup.Tomcat;
33  import org.apache.catalina.valves.AccessLogValve;
34  import org.apache.commons.io.IOUtils;
35  import org.apache.commons.lang.StringUtils;
36  import org.apache.maven.artifact.Artifact;
37  import org.apache.maven.artifact.factory.ArtifactFactory;
38  import org.apache.maven.artifact.repository.ArtifactRepository;
39  import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
40  import org.apache.maven.artifact.resolver.ArtifactResolutionException;
41  import org.apache.maven.artifact.resolver.ArtifactResolver;
42  import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
43  import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
44  import org.apache.maven.artifact.versioning.VersionRange;
45  import org.apache.maven.execution.MavenSession;
46  import org.apache.maven.plugin.MojoExecutionException;
47  import org.apache.maven.plugin.MojoFailureException;
48  import org.apache.maven.plugins.annotations.Component;
49  import org.apache.maven.plugins.annotations.Parameter;
50  import org.apache.maven.project.MavenProject;
51  import org.apache.maven.shared.filtering.MavenFileFilter;
52  import org.apache.maven.shared.filtering.MavenFileFilterRequest;
53  import org.apache.maven.shared.filtering.MavenFilteringException;
54  import org.apache.naming.NamingEntry;
55  import org.apache.naming.resources.FileDirContext;
56  import org.apache.tomcat.JarScanner;
57  import org.apache.tomcat.maven.common.config.AbstractWebapp;
58  import org.apache.tomcat.maven.common.run.EmbeddedRegistry;
59  import org.apache.tomcat.maven.common.run.ExternalRepositoriesReloadableWebappLoader;
60  import org.apache.tomcat.maven.plugin.tomcat7.AbstractTomcat7Mojo;
61  import org.apache.tomcat.util.scan.StandardJarScanner;
62  import org.codehaus.plexus.archiver.ArchiverException;
63  import org.codehaus.plexus.archiver.UnArchiver;
64  import org.codehaus.plexus.archiver.manager.ArchiverManager;
65  import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
66  import org.codehaus.plexus.classworlds.ClassWorld;
67  import org.codehaus.plexus.classworlds.realm.ClassRealm;
68  import org.codehaus.plexus.classworlds.realm.DuplicateRealmException;
69  import org.codehaus.plexus.util.DirectoryScanner;
70  import org.codehaus.plexus.util.FileUtils;
71  import org.w3c.dom.Document;
72  import org.w3c.dom.NamedNodeMap;
73  import org.w3c.dom.Node;
74  import org.xml.sax.SAXException;
75  
76  import javax.naming.NamingException;
77  import javax.servlet.ServletException;
78  import javax.xml.parsers.DocumentBuilder;
79  import javax.xml.parsers.DocumentBuilderFactory;
80  import javax.xml.parsers.ParserConfigurationException;
81  import javax.xml.stream.XMLInputFactory;
82  import javax.xml.stream.XMLStreamConstants;
83  import javax.xml.stream.XMLStreamException;
84  import javax.xml.stream.XMLStreamReader;
85  import java.io.File;
86  import java.io.FileInputStream;
87  import java.io.FileNotFoundException;
88  import java.io.FileOutputStream;
89  import java.io.IOException;
90  import java.net.MalformedURLException;
91  import java.net.URL;
92  import java.util.ArrayList;
93  import java.util.Collection;
94  import java.util.Collections;
95  import java.util.Iterator;
96  import java.util.List;
97  import java.util.Map;
98  import java.util.Properties;
99  import java.util.Set;
100 
101 /**
102  * @author Olivier Lamy
103  * @since 2.0
104  */
105 public abstract class AbstractRunMojo
106     extends AbstractTomcat7Mojo
107 {
108     // ---------------------------------------------------------------------
109     // Mojo Components
110     // ---------------------------------------------------------------------
111 
112     /**
113      * Used to look up Artifacts in the remote repository.
114      */
115     @Component
116     protected ArtifactFactory factory;
117 
118     /**
119      * Location of the local repository.
120      */
121     @Parameter( defaultValue = "${localRepository}", required = true, readonly = true )
122     private ArtifactRepository local;
123 
124     /**
125      * Used to look up Artifacts in the remote repository.
126      */
127     @Component
128     protected ArtifactResolver resolver;
129 
130     // ----------------------------------------------------------------------
131     // Mojo Parameters
132     // ----------------------------------------------------------------------
133 
134     /**
135      * The packaging of the Maven project that this goal operates upon.
136      */
137     @Parameter( defaultValue = "${project.packaging}", required = true, readonly = true )
138     private String packaging;
139 
140     /**
141      * The directory to create the Tomcat server configuration under.
142      */
143     @Parameter( defaultValue = "${project.build.directory}/tomcat" )
144     private File configurationDir;
145 
146     /**
147      * The port to run the Tomcat server on.
148      * Will be exposed as System props and session.executionProperties with key tomcat.maven.http.port
149      */
150     @Parameter( property = "maven.tomcat.port", defaultValue = "8080" )
151     private int port;
152 
153     /**
154      *
155      * this IP address will be used on all ports
156      * @since 2.2
157      */
158     @Parameter( property = "maven.tomcat.address")
159     private String address;
160 
161     /**
162      * The AJP port to run the Tomcat server on.
163      * By default it's 0 this means won't be started.
164      * The ajp connector will be started only for value > 0.
165      * Will be exposed as System props and session.executionProperties with key tomcat.maven.ajp.port
166      *
167      * @since 2.0
168      */
169     @Parameter( property = "maven.tomcat.ajp.port", defaultValue = "0" )
170     private int ajpPort;
171 
172     /**
173      * The AJP protocol to run the Tomcat server on.
174      * By default it's ajp.
175      * NOTE The ajp connector will be started only if {@link #ajpPort} > 0.
176      * possible values are:
177      * <ul>
178      * <li>org.apache.coyote.ajp.AjpProtocol - new blocking Java connector that supports an executor</li>
179      * <li>org.apache.coyote.ajp.AjpAprProtocol - the APR/native connector.</li>
180      * </ul>
181      *
182      * @since 2.0
183      */
184     @Parameter( property = "maven.tomcat.ajp.protocol", defaultValue = "org.apache.coyote.ajp.AjpProtocol" )
185     private String ajpProtocol;
186 
187     /**
188      * The https port to run the Tomcat server on.
189      * By default it's 0 this means won't be started.
190      * The https connector will be started only for value > 0.
191      * Will be exposed as System props and session.executionProperties with key tomcat.maven.https.port
192      *
193      * @since 1.0
194      */
195     @Parameter( property = "maven.tomcat.httpsPort", defaultValue = "0" )
196     private int httpsPort;
197 
198     /**
199      * The character encoding to use for decoding URIs.
200      *
201      * @since 1.0
202      */
203     @Parameter( property = "maven.tomcat.uriEncoding", defaultValue = "ISO-8859-1" )
204     private String uriEncoding;
205 
206     /**
207      * List of System properties to pass to the Tomcat Server.
208      *
209      * @since 1.0-alpha-2
210      */
211     @Parameter
212     private Map<String, String> systemProperties;
213 
214     /**
215      * The directory contains additional configuration Files that copied in the Tomcat conf Directory.
216      *
217      * @since 1.0-alpha-2
218      */
219     @Parameter( property = "maven.tomcat.additionalConfigFilesDir", defaultValue = "${basedir}/src/main/tomcatconf" )
220     private File additionalConfigFilesDir;
221 
222     /**
223      * server.xml to use <b>Note if you use this you must configure in this file your webapp paths</b>.
224      *
225      * @since 1.0-alpha-2
226      */
227     @Parameter( property = "maven.tomcat.serverXml" )
228     private File serverXml;
229 
230     /**
231      * overriding the providing web.xml to run tomcat
232      * <b>This override the global Tomcat web.xml located in $CATALINA_HOME/conf/</b>
233      *
234      * @since 1.0-alpha-2
235      */
236     @Parameter( property = "maven.tomcat.webXml" )
237     private File tomcatWebXml;
238 
239     /**
240      * Set this to true to allow Maven to continue to execute after invoking
241      * the run goal.
242      *
243      * @since 1.0
244      */
245     @Parameter( property = "maven.tomcat.fork", defaultValue = "false" )
246     private boolean fork;
247 
248     /**
249      * Will create a tomcat context for each dependencies of war type with 'scope' set to 'tomcat'.
250      * In other words, dependencies with:
251      * <pre>
252      *    &lt;type&gt;war&lt;/type&gt;
253      *    &lt;scope&gt;tomcat&lt;/scope&gt;
254      * </pre>
255      * To preserve backward compatibility it's false by default.
256      *
257      * @since 1.0
258      * @deprecated use webapps instead
259      */
260     @Parameter( property = "maven.tomcat.addContextWarDependencies", defaultValue = "false" )
261     private boolean addContextWarDependencies;
262 
263     /**
264      * The maven project.
265      *
266      * @since 1.0
267      */
268     @Component
269     protected MavenProject project;
270 
271     /**
272      * The archive manager.
273      *
274      * @since 1.0
275      */
276     @Component
277     private ArchiverManager archiverManager;
278 
279     /**
280      * if <code>true</code> a new classLoader separated from maven core will be created to start tomcat.
281      *
282      * @since 1.0
283      */
284     @Parameter( property = "tomcat.useSeparateTomcatClassLoader", defaultValue = "false" )
285     protected boolean useSeparateTomcatClassLoader;
286 
287     /**
288      * @since 1.0
289      */
290     @Parameter( defaultValue = "${plugin.artifacts}", required = true )
291     private List<Artifact> pluginArtifacts;
292 
293     /**
294      * If set to true ignore if packaging of project is not 'war'.
295      *
296      * @since 1.0
297      */
298     @Parameter( property = "tomcat.ignorePackaging", defaultValue = "false" )
299     private boolean ignorePackaging;
300 
301     /**
302      * Override the default keystoreFile for the HTTPS connector (if enabled)
303      *
304      * @since 1.1
305      */
306     @Parameter
307     private String keystoreFile;
308 
309     /**
310      * Override the default keystorePass for the HTTPS connector (if enabled)
311      *
312      * @since 1.1
313      */
314     @Parameter
315     private String keystorePass;
316 
317     /**
318      * Override the type of keystore file to be used for the server certificate. If not specified, the default value is "JKS".
319      *
320      * @since 2.0
321      */
322     @Parameter( defaultValue = "JKS" )
323     private String keystoreType;
324 
325     /**
326      * <p>
327      * Enables or disables naming support for the embedded Tomcat server.
328      * </p>
329      * <p>
330      * <strong>Note:</strong> This setting is ignored if you provide a <code>server.xml</code> for your
331      * Tomcat. Instead please configure naming in the <code>server.xml</code>.
332      * </p>
333      *
334      * @see <a href="http://tomcat.apache.org/tomcat-6.0-doc/api/org/apache/catalina/startup/Embedded.html">org.apache.catalina.startup.Embedded</a>
335      * @see <a href="http://tomcat.apache.org/tomcat-7.0-doc/api/org/apache/catalina/startup/Tomcat.html">org.apache.catalina.startup.Tomcat</a>
336      * @since 2.0
337      */
338     @Parameter( property = "maven.tomcat.useNaming", defaultValue = "true" )
339     private boolean useNaming;
340 
341     /**
342      * Force context scanning if you don't use a context file with reloadable = "true".
343      * The other way to use contextReloadable is to add attribute reloadable = "true"
344      * in your context file.
345      *
346      * @since 2.0
347      */
348     @Parameter( property = "maven.tomcat.contextReloadable", defaultValue = "false" )
349     protected boolean contextReloadable;
350 
351     /**
352      * represents the delay in seconds between each classPathScanning change invocation
353      *
354      * @see <a href="http://tomcat.apache.org/tomcat-7.0-doc/config/context.html">http://tomcat.apache.org/tomcat-7.0-doc/config/context.html</a>
355      */
356     @Parameter( property = "maven.tomcat.backgroundProcessorDelay", defaultValue = "-1" )
357     protected int backgroundProcessorDelay = -1;
358 
359 
360     /**
361      * <p>The path of the Tomcat context XML file.</p>
362      * <p>Since release 2.0, the file is filtered as a maven resource so you can use
363      * interpolation tokens ${ }</p>
364      */
365     @Parameter( property = "maven.tomcat.contextFile" )
366     protected File contextFile;
367 
368     /**
369      * The default context file to check for if contextFile not configured.
370      * If no contextFile configured and the below default not present, no
371      * contextFile will be sent to Tomcat, resulting in the latter's default
372      * context configuration being used instead.
373      */
374     @Parameter( defaultValue = "${project.build.directory}/${project.build.finalName}/META-INF/context.xml",
375                 readonly = true )
376     private File defaultContextFile;
377 
378     /**
379      * The protocol to run the Tomcat server on.
380      * By default it's HTTP/1.1.
381      * See possible values <a href="http://tomcat.apache.org/tomcat-7.0-doc/config/http.html">HTTP Connector</a>
382      * protocol attribute
383      *
384      * @since 2.0
385      */
386     @Parameter( property = "maven.tomcat.protocol", defaultValue = "HTTP/1.1" )
387     private String protocol;
388 
389     /**
390      * The path of the Tomcat users XML file.
391      *
392      * @since 2.0
393      */
394     @Parameter( property = "maven.tomcat.tomcatUsers.file" )
395     private File tomcatUsers;
396 
397     /**
398      * The path of the Tomcat logging configuration.
399      *
400      * @since 2.0
401      */
402     @Parameter( property = "maven.tomcat.tomcatLogging.file" )
403     private File tomcatLoggingFile;
404 
405     /**
406      * Skip execution
407      *
408      * @since 2.0
409      */
410     @Parameter( property = "maven.tomcat.skip", defaultValue = "false" )
411     protected boolean skip;
412 
413     /**
414      * Collection of webapp artifacts to be deployed. Elements are &lt;webapp&gt; and contain
415      * usual GAVC plus contextPath and/or contextFile elements.<p>
416      * @see {@link Webapp}
417      * @since 2.0
418      */
419     @Parameter
420     private List<Webapp> webapps;
421 
422     /**
423      * The static context
424      *
425      * @since 2.0
426      */
427     @Parameter( property = "maven.tomcat.staticContextPath", defaultValue = "/" )
428     private String staticContextPath;
429 
430     /**
431      * The static context docroot base fully qualified path
432      * if <code>null</code> static context won't be added
433      *
434      * @since 2.0
435      */
436     @Parameter( property = "maven.tomcat.staticContextDocbase" )
437     private String staticContextDocbase;
438 
439     /**
440      * Class loader class to set.
441      *
442      * @since 2.0
443      */
444     @Parameter
445     protected String classLoaderClass;
446 
447     @Parameter( defaultValue = "${session}", readonly = true, required = true )
448     protected MavenSession session;
449 
450     /**
451      * Will dump port in a properties file (see ports for property names).
452      * If empty no file generated
453      */
454     @Parameter( property = "maven.tomcat.propertiesPortFilePath" )
455     protected String propertiesPortFilePath;
456 
457     /**
458      * configure host name
459      *
460      * @since 2.0
461      */
462     @Parameter( property = "maven.tomcat.hostName", defaultValue = "localhost" )
463     protected String hostName;
464 
465     /**
466      * configure aliases
467      * see <a href="http://tomcat.apache.org/tomcat-7.0-doc/config/host.html#Host_Name_Aliases">Host Name aliases</a>
468      *
469      * @since 2.0
470      */
471     @Parameter
472     protected String[] aliases;
473 
474     /**
475      * enable client authentication for https (if configured)
476      * see <a href="http://tomcat.apache.org/tomcat-7.0-doc/config/http.html#SSL_Support_-_BIO_and_NIO">http://tomcat.apache.org/tomcat-7.0-doc/config/http.html#SSL_Support_-_BIO_and_NIO</a>
477      *
478      * @since 2.1
479      */
480     @Parameter( property = "maven.tomcat.https.clientAuth", defaultValue = "false" )
481     protected String clientAuth = "false";
482 
483     @Component( role = MavenFileFilter.class, hint = "default" )
484     protected MavenFileFilter mavenFileFilter;
485 
486 
487     /**
488      * In case a module in your reactors has some web-fragments they will be read.
489      * If you don't need that for performance reasons, you can deactivate it.
490      *
491      * @since 2.2
492      */
493     @Parameter( property = "maven.tomcat.jarScan.allDirectories", defaultValue = "true" )
494     protected boolean jarScanAllDirectories = true;
495 
496     /**
497      *
498      * @since 2.2
499      */
500     @Parameter( property = "maven.tomcat.useBodyEncodingForURI", defaultValue = "false" )
501     protected boolean useBodyEncodingForURI;
502 
503     /**
504      *
505      * @since 2.2
506      */
507     @Parameter
508     protected String trustManagerClassName;
509 
510     /**
511      *
512      * @since 2.2
513      */
514     @Parameter
515     protected String trustMaxCertLength;
516 
517     /**
518      *
519      * @since 2.2
520      */
521     @Parameter
522     protected String truststoreAlgorithm;
523 
524     /**
525      *
526      * @since 2.2
527      */
528     @Parameter
529     protected String truststoreFile;
530 
531     /**
532      *
533      * @since 2.2
534      */
535     @Parameter
536     protected String  truststorePass;
537 
538     /**
539      *
540      * @since 2.2
541      */
542     @Parameter
543     protected String truststoreProvider;
544 
545     /**
546      *
547      * @since 2.2
548      */
549     @Parameter
550     protected String truststoreType;
551 
552     // ----------------------------------------------------------------------
553     // Fields
554     // ----------------------------------------------------------------------
555 
556     /**
557      * @since 1.0
558      */
559     private ClassRealm tomcatRealm;
560 
561     // ----------------------------------------------------------------------
562     // Mojo Implementation
563     // ----------------------------------------------------------------------
564 
565     /**
566      * {@inheritDoc}
567      */
568     public void execute()
569         throws MojoExecutionException, MojoFailureException
570     {
571         if ( skip )
572         {
573             getLog().info( "skip execution" );
574             return;
575         }
576         // ensure project is a web application
577         if ( !isWar() && !addContextWarDependencies && getAdditionalWebapps().isEmpty() )
578         {
579             getLog().info( messagesProvider.getMessage( "AbstractRunMojo.nonWar" ) );
580             return;
581         }
582         ClassLoader originalClassLoader = null;
583         if ( useSeparateTomcatClassLoader )
584         {
585             originalClassLoader = Thread.currentThread().getContextClassLoader();
586         }
587         try
588         {
589             getLog().info( messagesProvider.getMessage( "AbstractRunMojo.runningWar", getWebappUrl() ) );
590 
591             initConfiguration();
592             startContainer();
593             if ( !fork )
594             {
595                 waitIndefinitely();
596             }
597         }
598         catch ( LifecycleException exception )
599         {
600             throw new MojoExecutionException( messagesProvider.getMessage( "AbstractRunMojo.cannotStart" ), exception );
601         }
602         catch ( IOException exception )
603         {
604             throw new MojoExecutionException(
605                 messagesProvider.getMessage( "AbstractRunMojo.cannotCreateConfiguration" ), exception );
606         }
607         catch ( ServletException e )
608         {
609             throw new MojoExecutionException( e.getMessage(), e );
610         }
611         catch ( MavenFilteringException e )
612         {
613             throw new MojoExecutionException( "filtering issue: " + e.getMessage(), e );
614         }
615         finally
616         {
617             if ( useSeparateTomcatClassLoader )
618             {
619                 Thread.currentThread().setContextClassLoader( originalClassLoader );
620             }
621         }
622     }
623 
624     // ----------------------------------------------------------------------
625     // Protected Methods
626     // ----------------------------------------------------------------------
627 
628     /**
629      * Gets the webapp context path to use for the web application being run.
630      *
631      * @return the webapp context path
632      */
633     protected String getPath()
634     {
635         return path;
636     }
637 
638     /**
639      * Gets the context to run this web application under for the specified embedded Tomcat.
640      *
641      * @param container the embedded Tomcat container being used
642      * @return the context to run this web application under
643      * @throws IOException            if the context could not be created
644      * @throws MojoExecutionException in case of an error creating the context
645      */
646     protected Context createContext( Tomcat container )
647         throws IOException, MojoExecutionException, ServletException
648     {
649         String contextPath = getPath();
650 
651         String baseDir = getDocBase().getAbsolutePath();
652 
653         File overriddenContextFile = getContextFile();
654 
655         StandardContext standardContext = null;
656 
657         if ( overriddenContextFile != null && overriddenContextFile.exists() )
658         {
659             standardContext = parseContextFile( overriddenContextFile );
660         }
661         else if ( defaultContextFile.exists() )
662         {
663             standardContext = parseContextFile( defaultContextFile );
664         }
665 
666         if ( standardContext != null )
667         {
668             if ( standardContext.getPath() != null )
669             {
670                 contextPath = standardContext.getPath();
671             }
672             if ( standardContext.getDocBase() != null )
673             {
674                 baseDir = standardContext.getDocBase();
675             }
676         }
677 
678         contextPath = "/".equals( contextPath ) ? "" : contextPath;
679 
680         getLog().info( "create webapp with contextPath: " + contextPath );
681 
682         Context context = container.addWebapp( contextPath, baseDir );
683 
684         context.setResources(
685             new MyDirContext( new File( project.getBuild().getOutputDirectory() ).getAbsolutePath() ) );
686 
687         if ( useSeparateTomcatClassLoader )
688         {
689             context.setParentClassLoader( getTomcatClassLoader() );
690         }
691 
692         final WebappLoader loader = createWebappLoader();
693 
694         context.setLoader( loader );
695 
696         if ( overriddenContextFile != null )
697         {
698             // here, send file to Tomcat for it to complain if missing
699             context.setConfigFile( overriddenContextFile.toURI().toURL() );
700         }
701         else if ( defaultContextFile.exists() )
702         {
703             // here, only sending default file if it indeed exists
704             // otherwise Tomcat will create a default context
705             context.setConfigFile( defaultContextFile.toURI().toURL() );
706         }
707 
708         if ( classLoaderClass != null )
709         {
710             loader.setLoaderClass( classLoaderClass );
711         }
712 
713         // https://issues.apache.org/jira/browse/MTOMCAT-239
714         // get the jar scanner to configure scanning directories as we can run a jar or a reactor project with a jar so
715         // the entries is a directory (target/classes)
716         JarScanner jarScanner = context.getJarScanner();
717 
718         // normally this one only but just in case ...
719         if ( jarScanner instanceof StandardJarScanner )
720         {
721             ( (StandardJarScanner) jarScanner ).setScanAllDirectories( jarScanAllDirectories );
722         }
723 
724         return context;
725 
726     }
727 
728     protected StandardContext parseContextFile( File file )
729         throws MojoExecutionException
730     {
731         try
732         {
733             StandardContext standardContext = new StandardContext();
734             XMLStreamReader reader = XMLInputFactory.newFactory().createXMLStreamReader( new FileInputStream( file ) );
735 
736             int tag = reader.next();
737 
738             while ( true )
739             {
740                 if ( tag == XMLStreamConstants.START_ELEMENT && StringUtils.equals( "Context", reader.getLocalName() ) )
741                 {
742                     String path = reader.getAttributeValue( null, "path" );
743                     if ( StringUtils.isNotBlank( path ) )
744                     {
745                         standardContext.setPath( path );
746                     }
747 
748                     String docBase = reader.getAttributeValue( null, "docBase" );
749                     if ( StringUtils.isNotBlank( docBase ) )
750                     {
751                         standardContext.setDocBase( docBase );
752                     }
753                 }
754                 if ( !reader.hasNext() )
755                 {
756                     break;
757                 }
758                 tag = reader.next();
759             }
760 
761             return standardContext;
762         }
763         catch ( XMLStreamException e )
764         {
765             throw new MojoExecutionException( e.getMessage(), e );
766         }
767         catch ( FileNotFoundException e )
768         {
769             throw new MojoExecutionException( e.getMessage(), e );
770         }
771     }
772 
773 
774     private static class MyDirContext
775         extends FileDirContext
776     {
777         String buildOutputDirectory;
778 
779         MyDirContext( String buildOutputDirectory )
780         {
781             this.buildOutputDirectory = buildOutputDirectory;
782         }
783 
784         @Override
785         protected List<NamingEntry> doListBindings( String name )
786             throws NamingException
787         {
788             if ( "/WEB-INF/classes".equals( name ) )
789             {
790                 if ( !new File( buildOutputDirectory ).exists() )
791                 {
792                     return Collections.emptyList();
793                 }
794                 FileDirContext fileDirContext = new FileDirContext();
795                 fileDirContext.setDocBase( buildOutputDirectory );
796                 NamingEntry namingEntry = new NamingEntry( "/WEB-INF/classes", fileDirContext, -1 );
797                 return Collections.singletonList( namingEntry );
798             }
799 
800             return super.doListBindings( name );
801         }
802     }
803 
804     /**
805      * Gets the webapp loader to run this web application under.
806      *
807      * @return the webapp loader to use
808      * @throws IOException            if the webapp loader could not be created
809      * @throws MojoExecutionException in case of an error creating the webapp loader
810      */
811     protected WebappLoader createWebappLoader()
812         throws IOException, MojoExecutionException
813     {
814         if ( useSeparateTomcatClassLoader )
815         {
816             return ( isContextReloadable() )
817                 ? new ExternalRepositoriesReloadableWebappLoader( getTomcatClassLoader(), getLog() )
818                 : new WebappLoader( getTomcatClassLoader() );
819         }
820 
821         return ( isContextReloadable() )
822             ? new ExternalRepositoriesReloadableWebappLoader( Thread.currentThread().getContextClassLoader(), getLog() )
823             : new WebappLoader( Thread.currentThread().getContextClassLoader() );
824     }
825 
826     /**
827      * Determine whether the passed context.xml file declares the context as reloadable or not.
828      *
829      * @return false by default, true if  reloadable="true" in context.xml.
830      */
831     protected boolean isContextReloadable()
832         throws MojoExecutionException
833     {
834         if ( contextReloadable || backgroundProcessorDelay > 0 )
835         {
836             return true;
837         }
838         // determine whether to use a reloadable Loader or not (default is false).
839         boolean reloadable = false;
840         try
841         {
842             if ( contextFile != null && contextFile.exists() )
843             {
844                 DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
845                 DocumentBuilder builder = builderFactory.newDocumentBuilder();
846                 Document contextDoc = builder.parse( contextFile );
847                 contextDoc.getDocumentElement().normalize();
848 
849                 NamedNodeMap nodeMap = contextDoc.getDocumentElement().getAttributes();
850                 Node reloadableAttribute = nodeMap.getNamedItem( "reloadable" );
851 
852                 reloadable =
853                     ( reloadableAttribute != null ) ? Boolean.valueOf( reloadableAttribute.getNodeValue() ) : false;
854             }
855             getLog().debug( "context reloadable: " + reloadable );
856         }
857         catch ( IOException ioe )
858         {
859             getLog().error( "Could not parse file: [" + contextFile.getAbsolutePath() + "]", ioe );
860         }
861         catch ( ParserConfigurationException pce )
862         {
863             getLog().error( "Could not configure XML parser", pce );
864         }
865         catch ( SAXException se )
866         {
867             getLog().error( "Could not parse file: [" + contextFile.getAbsolutePath() + "]", se );
868         }
869 
870         return reloadable;
871     }
872 
873 
874     /**
875      * Gets the webapp directory to run.
876      *
877      * @return the webapp directory
878      */
879     protected abstract File getDocBase()
880         throws IOException;
881 
882     /**
883      * Gets the Tomcat context XML file to use.
884      *
885      * @return the context XML file
886      */
887     protected abstract File getContextFile()
888         throws MojoExecutionException;
889 
890     // ----------------------------------------------------------------------
891     // Private Methods
892     // ----------------------------------------------------------------------
893 
894     /**
895      * Gets whether this project uses WAR packaging.
896      *
897      * @return whether this project uses WAR packaging
898      */
899     protected boolean isWar()
900     {
901         return "war".equals( packaging ) || ignorePackaging;
902     }
903 
904     /**
905      * Gets the URL of the running webapp.
906      *
907      * @return the URL of the running webapp
908      * @throws java.net.MalformedURLException if the running webapp URL is invalid
909      */
910     private URL getWebappUrl()
911         throws MalformedURLException
912     {
913         return new URL( "http", "localhost", port, getPath() );
914     }
915 
916     /**
917      * FIXME not sure we need all of those files with tomcat7
918      * Creates the Tomcat configuration directory with the necessary resources.
919      *
920      * @throws IOException            if the Tomcat configuration could not be created
921      * @throws MojoExecutionException if the Tomcat configuration could not be created
922      */
923     private void initConfiguration()
924         throws IOException, MojoExecutionException, MavenFilteringException
925     {
926         if ( configurationDir.exists() )
927         {
928             getLog().info( messagesProvider.getMessage( "AbstractRunMojo.usingConfiguration", configurationDir ) );
929         }
930         else
931         {
932             getLog().info( messagesProvider.getMessage( "AbstractRunMojo.creatingConfiguration", configurationDir ) );
933 
934             configurationDir.mkdirs();
935 
936             File confDir = new File( configurationDir, "conf" );
937             confDir.mkdir();
938 
939             if ( tomcatLoggingFile != null )
940             {
941                 FileUtils.copyFile( tomcatLoggingFile, new File( confDir, "logging.properties" ) );
942             }
943             else
944             {
945                 copyFile( "/conf/logging.properties", new File( confDir, "logging.properties" ) );
946             }
947 
948             copyFile( "/conf/tomcat-users.xml", new File( confDir, "tomcat-users.xml" ) );
949             if ( tomcatWebXml != null )
950             {
951                 if ( !tomcatWebXml.exists() )
952                 {
953                     throw new MojoExecutionException( " tomcatWebXml " + tomcatWebXml.getPath() + " not exists" );
954                 }
955                 //MTOMCAT-42  here it's a real file resources not a one coming with the mojo
956                 //MTOMCAT-128 apply filtering
957                 MavenFileFilterRequest mavenFileFilterRequest = new MavenFileFilterRequest();
958                 mavenFileFilterRequest.setFrom( tomcatWebXml );
959                 mavenFileFilterRequest.setTo( new File( confDir, "web.xml" ) );
960                 mavenFileFilterRequest.setMavenProject( project );
961                 mavenFileFilterRequest.setMavenSession( session );
962                 mavenFileFilterRequest.setFiltering( true );
963 
964                 mavenFileFilter.copyFile( mavenFileFilterRequest );
965 
966             }
967             else
968             {
969                 copyFile( "/conf/web.xml", new File( confDir, "web.xml" ) );
970             }
971 
972             File logDir = new File( configurationDir, "logs" );
973             logDir.mkdir();
974 
975             File webappsDir = new File( configurationDir, "webapps" );
976             webappsDir.mkdir();
977 
978             if ( additionalConfigFilesDir != null && additionalConfigFilesDir.exists() )
979             {
980                 DirectoryScanner scanner = new DirectoryScanner();
981                 scanner.addDefaultExcludes();
982                 scanner.setBasedir( additionalConfigFilesDir.getPath() );
983                 scanner.scan();
984 
985                 String[] files = scanner.getIncludedFiles();
986 
987                 if ( files != null && files.length > 0 )
988                 {
989                     getLog().info( "Coping additional tomcat config files" );
990 
991                     for ( int i = 0; i < files.length; i++ )
992                     {
993                         File file = new File( additionalConfigFilesDir, files[i] );
994 
995                         getLog().info( " copy " + file.getName() );
996 
997                         FileUtils.copyFileToDirectory( file, confDir );
998                     }
999                 }
1000             }
1001         }
1002     }
1003 
1004     /**
1005      * Copies the specified class resource to the specified file.
1006      *
1007      * @param fromPath the path of the class resource to copy
1008      * @param toFile   the file to copy to
1009      * @throws IOException if the file could not be copied
1010      */
1011     private void copyFile( String fromPath, File toFile )
1012         throws IOException
1013     {
1014         URL fromURL = getClass().getResource( fromPath );
1015 
1016         if ( fromURL == null )
1017         {
1018             throw new FileNotFoundException( fromPath );
1019         }
1020 
1021         FileUtils.copyURLToFile( fromURL, toFile );
1022     }
1023 
1024     /**
1025      * Starts the embedded Tomcat server.
1026      *
1027      * @throws IOException            if the server could not be configured
1028      * @throws LifecycleException     if the server could not be started
1029      * @throws MojoExecutionException if the server could not be configured
1030      */
1031     private void startContainer()
1032         throws IOException, LifecycleException, MojoExecutionException, ServletException
1033     {
1034         String previousCatalinaBase = System.getProperty( "catalina.base" );
1035 
1036         try
1037         {
1038 
1039             // Set the system properties
1040             setupSystemProperties();
1041 
1042             System.setProperty( "catalina.base", configurationDir.getAbsolutePath() );
1043 
1044             if ( serverXml != null )
1045             {
1046                 if ( !serverXml.exists() )
1047                 {
1048                     throw new MojoExecutionException( serverXml.getPath() + " not exists" );
1049                 }
1050 
1051                 Catalina container = new Catalina();
1052 
1053                 if ( useSeparateTomcatClassLoader )
1054                 {
1055                     Thread.currentThread().setContextClassLoader( getTomcatClassLoader() );
1056                     container.setParentClassLoader( getTomcatClassLoader() );
1057                 }
1058 
1059                 container.setUseNaming( this.useNaming );
1060                 container.setConfig( serverXml.getAbsolutePath() );
1061                 container.start();
1062                 EmbeddedRegistry.getInstance().register( container );
1063             }
1064             else
1065             {
1066 
1067                 System.setProperty( "java.util.logging.manager", "org.apache.juli.ClassLoaderLogManager" );
1068                 System.setProperty( "java.util.logging.config.file",
1069                                     new File( configurationDir, "conf/logging.properties" ).toString() );
1070 
1071                 // Trigger loading of catalina.properties
1072                 CatalinaProperties.getProperty( "foo" );
1073 
1074                 Tomcat embeddedTomcat = new ExtendedTomcat( configurationDir );
1075 
1076                 embeddedTomcat.setBaseDir( configurationDir.getAbsolutePath() );
1077                 MemoryRealm memoryRealm = new MemoryRealm();
1078 
1079                 if ( tomcatUsers != null )
1080                 {
1081                     if ( !tomcatUsers.exists() )
1082                     {
1083                         throw new MojoExecutionException( " tomcatUsers " + tomcatUsers.getPath() + " not exists" );
1084                     }
1085                     getLog().info( "use tomcat-users.xml from " + tomcatUsers.getAbsolutePath() );
1086                     memoryRealm.setPathname( tomcatUsers.getAbsolutePath() );
1087                 }
1088 
1089                 embeddedTomcat.setDefaultRealm( memoryRealm );
1090 
1091                 Context ctx = createContext( embeddedTomcat );
1092 
1093                 if ( useNaming )
1094                 {
1095                     embeddedTomcat.enableNaming();
1096                 }
1097 
1098                 embeddedTomcat.getHost().setAppBase( new File( configurationDir, "webapps" ).getAbsolutePath() );
1099 
1100                 if ( hostName != null )
1101                 {
1102                     embeddedTomcat.getHost().setName( hostName );
1103                 }
1104                 if ( aliases != null )
1105                 {
1106                     for ( String alias : aliases )
1107                     {
1108                         embeddedTomcat.getHost().addAlias( alias );
1109                     }
1110 
1111                 }
1112                 createStaticContext( embeddedTomcat, ctx, embeddedTomcat.getHost() );
1113 
1114                 Connector connector = new Connector( protocol );
1115                 connector.setPort( port );
1116 
1117                 if ( httpsPort > 0 )
1118                 {
1119                     connector.setRedirectPort( httpsPort );
1120                 }
1121 
1122                 if ( address != null)
1123                 {
1124                     connector.setAttribute( "address", address );
1125                 }
1126 
1127                 connector.setURIEncoding( uriEncoding );
1128 
1129                 connector.setUseBodyEncodingForURI( this.useBodyEncodingForURI );
1130 
1131                 embeddedTomcat.getService().addConnector( connector );
1132 
1133                 embeddedTomcat.setConnector( connector );
1134 
1135                 AccessLogValve alv = new AccessLogValve();
1136                 alv.setDirectory( new File( configurationDir, "logs" ).getAbsolutePath() );
1137                 alv.setPattern( "%h %l %u %t \"%r\" %s %b %I %D" );
1138                 embeddedTomcat.getHost().getPipeline().addValve( alv );
1139 
1140                 // create https connector
1141                 Connector httpsConnector = null;
1142                 if ( httpsPort > 0 )
1143                 {
1144                     httpsConnector = new Connector( protocol );
1145                     httpsConnector.setPort( httpsPort );
1146                     httpsConnector.setSecure( true );
1147                     httpsConnector.setProperty( "SSLEnabled", "true" );
1148                     // should be default but configure it anyway
1149                     httpsConnector.setProperty( "sslProtocol", "TLS" );
1150                     if ( keystoreFile != null )
1151                     {
1152                         httpsConnector.setAttribute( "keystoreFile", keystoreFile );
1153                     }
1154                     if ( keystorePass != null )
1155                     {
1156                         httpsConnector.setAttribute( "keystorePass", keystorePass );
1157                     }
1158                     if ( keystoreType != null )
1159                     {
1160                         httpsConnector.setAttribute( "keystoreType", keystoreType );
1161                     }
1162 
1163                     if( trustManagerClassName != null )
1164                     {
1165                         httpsConnector.setAttribute( "trustManagerClassName", trustManagerClassName );
1166                     }
1167 
1168                     if( trustMaxCertLength != null )
1169                     {
1170                         httpsConnector.setAttribute( "trustMaxCertLength", trustMaxCertLength );
1171                     }
1172 
1173                     if( truststoreAlgorithm != null )
1174                     {
1175                         httpsConnector.setAttribute( "truststoreAlgorithm", truststoreAlgorithm );
1176                     }
1177 
1178                     if( truststoreFile != null )
1179                     {
1180                         httpsConnector.setAttribute( "truststoreFile", truststoreFile );
1181                     }
1182 
1183                     if( truststorePass != null )
1184                     {
1185                         httpsConnector.setAttribute( "truststorePass", truststorePass );
1186                     }
1187 
1188                     if( truststoreProvider != null )
1189                     {
1190                         httpsConnector.setAttribute( "truststoreProvider", truststoreProvider );
1191                     }
1192 
1193 
1194                     if( truststoreType != null )
1195                     {
1196                         httpsConnector.setAttribute( "truststoreType", truststoreType );
1197                     }
1198 
1199                     httpsConnector.setAttribute( "clientAuth", clientAuth );
1200 
1201                     httpsConnector.setUseBodyEncodingForURI( this.useBodyEncodingForURI );
1202 
1203                     if ( address != null)
1204                     {
1205                         httpsConnector.setAttribute( "address", address );
1206                     }
1207 
1208                     embeddedTomcat.getEngine().getService().addConnector( httpsConnector );
1209 
1210                 }
1211 
1212                 // create ajp connector
1213                 Connector ajpConnector = null;
1214                 if ( ajpPort > 0 )
1215                 {
1216                     ajpConnector = new Connector( ajpProtocol );
1217                     ajpConnector.setPort( ajpPort );
1218                     ajpConnector.setURIEncoding( uriEncoding );
1219                     ajpConnector.setUseBodyEncodingForURI( this.useBodyEncodingForURI );
1220                     if ( address != null)
1221                     {
1222                         ajpConnector.setAttribute( "address", address );
1223                     }
1224                     embeddedTomcat.getEngine().getService().addConnector( ajpConnector );
1225                 }
1226 
1227                 if ( addContextWarDependencies || !getAdditionalWebapps().isEmpty() )
1228                 {
1229                     createDependencyContexts( embeddedTomcat );
1230                 }
1231 
1232                 if ( useSeparateTomcatClassLoader )
1233                 {
1234                     Thread.currentThread().setContextClassLoader( getTomcatClassLoader() );
1235                     embeddedTomcat.getEngine().setParentClassLoader( getTomcatClassLoader() );
1236                 }
1237 
1238                 embeddedTomcat.start();
1239 
1240                 Properties portProperties = new Properties();
1241 
1242                 portProperties.put( "tomcat.maven.http.port", Integer.toString( connector.getLocalPort() ) );
1243 
1244                 session.getExecutionProperties().put( "tomcat.maven.http.port",
1245                                                       Integer.toString( connector.getLocalPort() ) );
1246                 System.setProperty( "tomcat.maven.http.port", Integer.toString( connector.getLocalPort() ) );
1247 
1248                 if ( httpsConnector != null )
1249                 {
1250                     session.getExecutionProperties().put( "tomcat.maven.https.port",
1251                                                           Integer.toString( httpsConnector.getLocalPort() ) );
1252                     portProperties.put( "tomcat.maven.https.port", Integer.toString( httpsConnector.getLocalPort() ) );
1253                     System.setProperty( "tomcat.maven.https.port", Integer.toString( httpsConnector.getLocalPort() ) );
1254                 }
1255 
1256                 if ( ajpConnector != null )
1257                 {
1258                     session.getExecutionProperties().put( "tomcat.maven.ajp.port",
1259                                                           Integer.toString( ajpConnector.getLocalPort() ) );
1260                     portProperties.put( "tomcat.maven.ajp.port", Integer.toString( ajpConnector.getLocalPort() ) );
1261                     System.setProperty( "tomcat.maven.ajp.port", Integer.toString( ajpConnector.getLocalPort() ) );
1262                 }
1263                 if ( propertiesPortFilePath != null )
1264                 {
1265                     File propertiesPortsFile = new File( propertiesPortFilePath );
1266                     if ( propertiesPortsFile.exists() )
1267                     {
1268                         propertiesPortsFile.delete();
1269                     }
1270                     FileOutputStream fileOutputStream = new FileOutputStream( propertiesPortsFile );
1271                     try
1272                     {
1273                         portProperties.store( fileOutputStream, "Apache Tomcat Maven plugin port used" );
1274                     }
1275                     finally
1276                     {
1277                         IOUtils.closeQuietly( fileOutputStream );
1278                     }
1279                 }
1280 
1281                 EmbeddedRegistry.getInstance().register( embeddedTomcat );
1282 
1283             }
1284 
1285 
1286         }
1287         finally
1288         {
1289             if ( previousCatalinaBase != null )
1290             {
1291                 System.setProperty( "catalina.base", previousCatalinaBase );
1292             }
1293         }
1294     }
1295 
1296     private List<Webapp> getAdditionalWebapps()
1297     {
1298         if ( webapps == null )
1299         {
1300             return Collections.emptyList();
1301         }
1302         return webapps;
1303     }
1304 
1305     protected ClassRealm getTomcatClassLoader()
1306         throws MojoExecutionException
1307     {
1308         if ( this.tomcatRealm != null )
1309         {
1310             return tomcatRealm;
1311         }
1312         try
1313         {
1314             ClassWorld world = new ClassWorld();
1315             ClassRealm root = world.newRealm( "tomcat", Thread.currentThread().getContextClassLoader() );
1316 
1317             for ( @SuppressWarnings( "rawtypes" ) Iterator i = pluginArtifacts.iterator(); i.hasNext(); )
1318             {
1319                 Artifact pluginArtifact = (Artifact) i.next();
1320                 // add all plugin artifacts see https://issues.apache.org/jira/browse/MTOMCAT-122
1321                 if ( pluginArtifact.getFile() != null )
1322                 {
1323                     root.addURL( pluginArtifact.getFile().toURI().toURL() );
1324                 }
1325 
1326             }
1327             tomcatRealm = root;
1328             return root;
1329         }
1330         catch ( DuplicateRealmException e )
1331         {
1332             throw new MojoExecutionException( e.getMessage(), e );
1333         }
1334         catch ( MalformedURLException e )
1335         {
1336             throw new MojoExecutionException( e.getMessage(), e );
1337         }
1338     }
1339 
1340     @SuppressWarnings( "unchecked" )
1341     public Set<Artifact> getProjectArtifacts()
1342     {
1343         return project.getArtifacts();
1344     }
1345 
1346     /**
1347      * Causes the current thread to wait indefinitely. This method does not return.
1348      */
1349     private void waitIndefinitely()
1350     {
1351         Object lock = new Object();
1352 
1353         synchronized ( lock )
1354         {
1355             try
1356             {
1357                 lock.wait();
1358             }
1359             catch ( InterruptedException exception )
1360             {
1361                 getLog().warn( messagesProvider.getMessage( "AbstractRunMojo.interrupted" ), exception );
1362             }
1363         }
1364     }
1365 
1366 
1367     /**
1368      * Set the SystemProperties from the configuration.
1369      */
1370     private void setupSystemProperties()
1371     {
1372         if ( systemProperties != null && !systemProperties.isEmpty() )
1373         {
1374             getLog().info( "setting SystemProperties:" );
1375 
1376             for ( String key : systemProperties.keySet() )
1377             {
1378                 String value = systemProperties.get( key );
1379 
1380                 if ( value != null )
1381                 {
1382                     getLog().info( " " + key + "=" + value );
1383                     System.setProperty( key, value );
1384                 }
1385                 else
1386                 {
1387                     getLog().info( "skip sysProps " + key + " with empty value" );
1388                 }
1389             }
1390         }
1391     }
1392 
1393 
1394     /**
1395      * Allows the startup of additional webapps in the tomcat container by declaration with scope
1396      * "tomcat".
1397      *
1398      * @param container tomcat
1399      * @return dependency tomcat contexts of warfiles in scope "tomcat"
1400      */
1401     private Collection<Context> createDependencyContexts( Tomcat container )
1402         throws MojoExecutionException, MalformedURLException, ServletException, IOException
1403     {
1404         getLog().info( "Deploying dependency wars" );
1405         // Let's add other modules
1406         List<Context> contexts = new ArrayList<Context>();
1407 
1408         ScopeArtifactFilter filter = new ScopeArtifactFilter( "tomcat" );
1409         @SuppressWarnings( "unchecked" ) Set<Artifact> artifacts = project.getArtifacts();
1410         for ( Artifact artifact : artifacts )
1411         {
1412 
1413             // Artifact is not yet registered and it has neither test, nor a
1414             // provided scope, not is it optional
1415             if ( "war".equals( artifact.getType() ) && !artifact.isOptional() && filter.include( artifact ) )
1416             {
1417                 addContextFromArtifact( container, contexts, artifact, "/" + artifact.getArtifactId(), null, false );
1418             }
1419         }
1420 
1421         for ( AbstractWebapp additionalWebapp : getAdditionalWebapps() )
1422         {
1423             String contextPath = additionalWebapp.getContextPath();
1424             if ( !contextPath.startsWith( "/" ) )
1425             {
1426                 contextPath = "/" + contextPath;
1427             }
1428             addContextFromArtifact( container, contexts, getArtifact( additionalWebapp ), contextPath,
1429                                     additionalWebapp.getContextFile(), additionalWebapp.isAsWebapp() );
1430         }
1431         return contexts;
1432     }
1433 
1434 
1435     private void addContextFromArtifact( Tomcat container, List<Context> contexts, Artifact artifact,
1436                                          String contextPath, File contextXml, boolean asWebApp )
1437         throws MojoExecutionException, ServletException, IOException
1438     {
1439         getLog().info( "Deploy warfile: " + String.valueOf( artifact.getFile() ) + " to contextPath: " + contextPath );
1440         File webapps = new File( configurationDir, "webapps" );
1441         File artifactWarDir = new File( webapps, artifact.getArtifactId() );
1442         if ( !artifactWarDir.exists() )
1443         {
1444             //dont extract if exists
1445             artifactWarDir.mkdir();
1446             try
1447             {
1448                 UnArchiver unArchiver = archiverManager.getUnArchiver( "zip" );
1449                 unArchiver.setSourceFile( artifact.getFile() );
1450                 unArchiver.setDestDirectory( artifactWarDir );
1451 
1452                 // Extract the module
1453                 unArchiver.extract();
1454             }
1455             catch ( NoSuchArchiverException e )
1456             {
1457                 getLog().error( e );
1458                 return;
1459             }
1460             catch ( ArchiverException e )
1461             {
1462                 getLog().error( e );
1463                 return;
1464             }
1465         }
1466         // TODO make that configurable ?
1467         //WebappLoader webappLoader = new WebappLoader( Thread.currentThread().getContextClassLoader() );
1468         WebappLoader webappLoader = createWebappLoader();
1469         Context context = null;
1470         if ( asWebApp )
1471         {
1472             context = container.addWebapp( contextPath, artifactWarDir.getAbsolutePath() );
1473         }
1474         else
1475         {
1476             context = container.addContext( contextPath, artifactWarDir.getAbsolutePath() );
1477         }
1478         context.setLoader( webappLoader );
1479 
1480         File contextFile = contextXml != null ? contextXml : getContextFile();
1481         if ( contextFile != null )
1482         {
1483             context.setConfigFile( contextFile.toURI().toURL() );
1484         }
1485 
1486         contexts.add( context );
1487 //        container.getHost().addChild(context);
1488     }
1489 
1490     private void createStaticContext( final Tomcat container, Context context, Host host )
1491     {
1492         if ( staticContextDocbase != null )
1493         {
1494             Context staticContext = container.addContext( staticContextPath, staticContextDocbase );
1495             staticContext.setPrivileged( true );
1496             Wrapper servlet = context.createWrapper();
1497             servlet.setServletClass( DefaultServlet.class.getName() );
1498             servlet.setName( "staticContent" );
1499             staticContext.addChild( servlet );
1500             staticContext.addServletMapping( "/", "staticContent" );
1501             host.addChild( staticContext );
1502         }
1503     }
1504 
1505 
1506     /**
1507      * Resolves the Artifact from the remote repository if necessary. If no version is specified, it will be retrieved
1508      * from the dependency list or from the DependencyManagement section of the pom.
1509      *
1510      * @param additionalWebapp containing information about artifact from plugin configuration.
1511      * @return Artifact object representing the specified file.
1512      * @throws MojoExecutionException with a message if the version can't be found in DependencyManagement.
1513      */
1514     protected Artifact getArtifact( AbstractWebapp additionalWebapp )
1515         throws MojoExecutionException
1516     {
1517 
1518         Artifact artifact;
1519         VersionRange vr;
1520         try
1521         {
1522             vr = VersionRange.createFromVersionSpec( additionalWebapp.getVersion() );
1523         }
1524         catch ( InvalidVersionSpecificationException e )
1525         {
1526             getLog().warn( "fail to create versionRange from version: " + additionalWebapp.getVersion(), e );
1527             vr = VersionRange.createFromVersion( additionalWebapp.getVersion() );
1528         }
1529 
1530         if ( StringUtils.isEmpty( additionalWebapp.getClassifier() ) )
1531         {
1532             artifact =
1533                 factory.createDependencyArtifact( additionalWebapp.getGroupId(), additionalWebapp.getArtifactId(), vr,
1534                                                   additionalWebapp.getType(), null, Artifact.SCOPE_COMPILE );
1535         }
1536         else
1537         {
1538             artifact =
1539                 factory.createDependencyArtifact( additionalWebapp.getGroupId(), additionalWebapp.getArtifactId(), vr,
1540                                                   additionalWebapp.getType(), additionalWebapp.getClassifier(),
1541                                                   Artifact.SCOPE_COMPILE );
1542         }
1543 
1544         try
1545         {
1546             resolver.resolve( artifact, project.getRemoteArtifactRepositories(), this.local );
1547         }
1548         catch ( ArtifactResolutionException e )
1549         {
1550             throw new MojoExecutionException( "Unable to resolve artifact.", e );
1551         }
1552         catch ( ArtifactNotFoundException e )
1553         {
1554             throw new MojoExecutionException( "Unable to find artifact.", e );
1555         }
1556 
1557         return artifact;
1558     }
1559 }