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