View Javadoc

1   package org.codehaus.mojo.tomcat;
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 java.io.File;
24  import java.io.FileNotFoundException;
25  import java.io.IOException;
26  import java.net.InetAddress;
27  import java.net.MalformedURLException;
28  import java.net.URL;
29  import java.util.ArrayList;
30  import java.util.Collection;
31  import java.util.Iterator;
32  import java.util.List;
33  import java.util.Map;
34  import java.util.Set;
35  
36  import org.apache.catalina.Context;
37  import org.apache.catalina.Engine;
38  import org.apache.catalina.Host;
39  import org.apache.catalina.LifecycleException;
40  import org.apache.catalina.connector.Connector;
41  import org.apache.catalina.loader.WebappLoader;
42  import org.apache.catalina.realm.MemoryRealm;
43  import org.apache.catalina.startup.Catalina;
44  import org.apache.catalina.startup.Embedded;
45  import org.apache.maven.artifact.Artifact;
46  import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
47  import org.apache.maven.plugin.MojoExecutionException;
48  import org.apache.maven.plugin.MojoFailureException;
49  import org.apache.maven.project.MavenProject;
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  
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  
69  /**
70   * Abstract goal that provides common configuration for embedded Tomcat goals.
71   * 
72   * @author Jurgen Lust
73   * @author Mark Hobson <markhobson@gmail.com>
74   * @version $Id: AbstractRunMojo.java 14033 2011-05-04 17:51:15Z bimargulies $
75   */
76  public abstract class AbstractRunMojo
77      extends AbstractI18NMojo
78  {
79      // ----------------------------------------------------------------------
80      // Mojo Parameters
81      // ----------------------------------------------------------------------
82  
83      /**
84       * The packaging of the Maven project that this goal operates upon.
85       * 
86       * @parameter expression = "${project.packaging}"
87       * @required
88       * @readonly
89       */
90      private String packaging;
91  
92      /**
93       * The directory to create the Tomcat server configuration under.
94       * 
95       * @parameter expression="${project.build.directory}/tomcat"
96       */
97      private File configurationDir;
98  
99      /**
100      * The port to run the Tomcat server on.
101      * 
102      * @parameter expression="${maven.tomcat.port}" default-value="8080"
103      */
104     private int port;
105     
106     /**
107      * The AJP port to run the Tomcat server on.
108      * By default it's 0 this means won't be started. 
109      * The ajp connector will be started only for value > 0. 
110      * @since 1.2
111      * @parameter expression="${maven.tomcat.ajp.port}" default-value="0"
112      */
113     private int ajpPort;
114     
115     /**
116      * The AJP protocol to run the Tomcat server on.
117      * By default it's ajp. 
118      * NOTE The ajp connector will be started only if {@link #ajpPort} > 0. 
119      * @since 1.2
120      * @parameter expression="${maven.tomcat.ajp.protocol}" default-value="ajp"
121      */    
122     private String ajpProtocol;
123     
124     /**
125      * The https port to run the Tomcat server on.
126      * By default it's 0 this means won't be started. 
127      * The https connector will be started only for value > 0. 
128      * @since 1.0
129      * @parameter expression="${maven.tomcat.httpsPort}" default-value="0"
130      */
131     private int httpsPort;
132 
133     /**
134      * The character encoding to use for decoding URIs.
135      * @since 1.0
136      * @parameter expression="${maven.tomcat.uriEncoding}" default-value="ISO-8859-1"
137      */
138     private String uriEncoding;
139 
140     /**
141      * List of System properties to pass to the Tomcat Server.
142      * 
143      * @parameter
144      * @since 1.0-alpha-2
145      */
146     private Map<String, String> systemProperties;
147 
148     /**
149      * The directory contains additional configuration Files that copied in the Tomcat conf Directory.
150      * 
151      * @parameter expression = "${maven.tomcat.additionalConfigFilesDir}" default-value="${basedir}/src/main/tomcatconf"
152      * @since 1.0-alpha-2
153      */
154     private File additionalConfigFilesDir;
155 
156     /**
157      * server.xml to use <b>Note if you use this you must configure in this file your webapp paths</b>.
158      * 
159      * @parameter expression="${maven.tomcat.serverXml}"
160      * @since 1.0-alpha-2
161      */
162     private File serverXml;
163 
164     /**
165      * overriding the providing web.xml to run tomcat
166      * 
167      * @parameter expression="${maven.tomcat.webXml}"
168      * @since 1.0-alpha-2
169      */
170     private File tomcatWebXml;
171     
172     /**
173      * Set this to true to allow Maven to continue to execute after invoking 
174      * the run goal.
175      * 
176      * @parameter expression="${maven.tomcat.fork}" default-value="false"
177      * @since 1.0
178      */
179     private boolean fork;
180     
181     /**
182      * Will create a tomcat context for each dependencies of war type with 'scope' set to 'tomcat'.
183      * In other words, dependencies with:
184      * <pre>
185      *    &lt;type&gt;war&lt;/type&gt;
186      *    &lt;scope&gt;tomcat&lt;/scope&gt;
187      * </pre>
188      * To preserve backward compatibility it's false by default.
189      * @parameter expression="${maven.tomcat.addContextWarDependencies}" default-value="false"
190      * @since 1.0
191      */
192     private boolean addContextWarDependencies;
193         
194     /**
195      * The maven project.
196      *
197      * @parameter expression="${project}"
198      * @since 1.0
199      * @required
200      * @readonly
201      */
202     protected MavenProject project;
203 
204     /**
205      * The archive manager.
206      * @since 1.0
207      * @component
208      */
209     private ArchiverManager archiverManager;
210     
211     /**
212      * if <code>true</code> a new classLoader separated from maven core will be created to start tomcat.
213      * @parameter expression="${tomcat.useSeparateTomcatClassLoader}" default-value="false"
214      * @since 1.0
215      */
216     protected boolean useSeparateTomcatClassLoader;
217         
218     /**
219      * @parameter expression="${plugin.artifacts}"
220      * @required
221      * @since 1.0
222      */
223     @SuppressWarnings("rawtypes")
224     private List pluginArtifacts;
225    
226     /**
227      * If set to true ignore if packaging of project is not 'war'.
228      * @since 1.0
229      * @parameter expression="${tomcat.ignorePackaging}" default-value="false"
230      */
231     private boolean ignorePackaging;
232 
233     /**
234      * Override the default keystoreFile for the HTTPS connector (if enabled)
235      * @since 1.1
236      * @parameter
237      */
238     private String keystoreFile;
239 
240     /**
241      * Override the default keystorePass for the HTTPS connector (if enabled)
242      * @since 1.1
243      * @parameter
244      */
245     private String keystorePass;
246 
247     /**
248      * <p>
249      * Enables or disables naming support for the embedded Tomcat server. By default the embedded Tomcat
250      * in Tomcat 6 comes with naming enabled. In contrast to this the embedded Tomcat 7 comes with
251      * naming disabled by default.
252      * </p>
253      * <p>
254      * With release 1.2 of this plugin useNaming defaults to false as it does for Tomcat 7. To enable it,
255      * set this parameter to true or set the property <code>maven.tomcat.useNaming</code>.
256      * </p>
257      * <p>
258      * <strong>Note:</strong> This setting is ignored if you provide a <code>server.xml</code> for your
259      * Tomcat. Instead please configure naming in the <code>server.xml</code>.
260      * </p>
261      * @see <a href="http://tomcat.apache.org/tomcat-6.0-doc/api/org/apache/catalina/startup/Embedded.html">org.apache.catalina.startup.Embedded</a>
262      * @see <a href="http://tomcat.apache.org/tomcat-7.0-doc/api/org/apache/catalina/startup/Tomcat.html">org.apache.catalina.startup.Tomcat</a>
263      * @parameter expression="${maven.tomcat.useNaming}" default-value="false"
264      * @since 1.2
265      * @todo adopt documentation once Tomcat 7 is supported (MTOMCAT-62)
266      */
267     private boolean useNaming;
268     
269     /**
270      * Force context scanning if you don't use a context file with reloadable = "true".
271      * The other way to use contextReloadable is to add attribute reloadable = "true"
272      * in your context file.
273      * @parameter expression="${maven.tomcat.contextReloadable}" default-value="false"
274      * @since 1.2
275      */
276     protected boolean contextReloadable;
277     
278 
279     /**
280      * The path of the Tomcat context XML file.
281      * 
282      * @parameter expression = "src/main/webapp/META-INF/context.xml"
283      */
284     protected File contextFile;    
285 
286     // ----------------------------------------------------------------------
287     // Fields
288     // ----------------------------------------------------------------------
289 
290     /**
291      * @since 1.0
292      */
293     private ClassRealm tomcatRealm;
294     
295     // ----------------------------------------------------------------------
296     // Mojo Implementation
297     // ----------------------------------------------------------------------
298 
299     /**
300      * {@inheritDoc}
301      */
302     public void execute()
303         throws MojoExecutionException, MojoFailureException
304     {
305         // ensure project is a web application
306         if ( !isWar() )
307         {
308             getLog().info( getMessage( "AbstractRunMojo.nonWar" ) );
309             return;
310         }
311         ClassLoader originalClassLoaser = Thread.currentThread().getContextClassLoader();
312         try
313         {
314             if ( useSeparateTomcatClassLoader )
315             {
316                 Thread.currentThread().setContextClassLoader( getTomcatClassLoader() );
317             }
318             getLog().info( getMessage( "AbstractRunMojo.runningWar", getWebappUrl() ) );
319 
320             initConfiguration();
321             startContainer();
322             if ( !fork )
323             {
324                 waitIndefinitely();
325             }
326         }
327         catch ( LifecycleException exception )
328         {
329             throw new MojoExecutionException( getMessage( "AbstractRunMojo.cannotStart" ), exception );
330         }
331         catch ( IOException exception )
332         {
333             throw new MojoExecutionException( getMessage( "AbstractRunMojo.cannotCreateConfiguration" ), exception );
334         } finally
335         {
336             if (useSeparateTomcatClassLoader)
337             {
338                 Thread.currentThread().setContextClassLoader( originalClassLoaser );
339             }
340         }
341     }
342 
343     // ----------------------------------------------------------------------
344     // Protected Methods
345     // ----------------------------------------------------------------------
346 
347     /**
348      * Gets the webapp context path to use for the web application being run.
349      * 
350      * @return the webapp context path
351      */
352     protected String getPath()
353     {
354         return path;
355     }
356 
357     /**
358      * Gets the context to run this web application under for the specified embedded Tomcat.
359      * 
360      * @param container the embedded Tomcat container being used
361      * @return the context to run this web application under
362      * @throws IOException if the context could not be created
363      * @throws MojoExecutionException in case of an error creating the context
364      */
365     protected Context createContext( Embedded container )
366         throws IOException, MojoExecutionException
367     {
368         String contextPath = getPath();
369         Context context = container.createContext( "/".equals( contextPath ) ? "" : contextPath, getDocBase()
370             .getAbsolutePath() );
371 
372         if ( useSeparateTomcatClassLoader )
373         {
374             context.setParentClassLoader( getTomcatClassLoader() );
375         }
376 
377         context.setLoader( createWebappLoader() );
378         File contextFile = getContextFile();
379         if (contextFile != null)
380         {
381             context.setConfigFile( getContextFile().getAbsolutePath() );
382         }
383         return context;
384     }
385 
386     /**
387      * Gets the webapp loader to run this web application under.
388      * 
389      * @return the webapp loader to use
390      * @throws IOException if the webapp loader could not be created
391      * @throws MojoExecutionException in case of an error creating the webapp loader
392      */
393     protected WebappLoader createWebappLoader()
394         throws IOException, MojoExecutionException
395     {
396         if ( useSeparateTomcatClassLoader )
397         {
398             return ( isContextReloadable() )
399                     ? new ExternalRepositoriesReloadableWebappLoader( getTomcatClassLoader(), getLog() )
400                     : new WebappLoader( getTomcatClassLoader() );
401         }
402 
403         return ( isContextReloadable() )
404                 ? new ExternalRepositoriesReloadableWebappLoader( Thread.currentThread().getContextClassLoader(), getLog()  )
405                 : new WebappLoader( Thread.currentThread().getContextClassLoader() );
406     }
407 
408     /**
409      * Determine whether the passed context.xml file declares the context as reloadable or not.
410      * @return false by default, true if  reloadable="true" in context.xml.
411      */
412     protected boolean isContextReloadable() throws MojoExecutionException
413     {
414         if ( contextReloadable )
415         {
416             return true;
417         }
418         // determine whether to use a reloadable Loader or not (default is false).
419         boolean reloadable = false;
420         try
421         {
422             if ( contextFile !=null && contextFile.exists() )
423             {
424                 DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
425                 DocumentBuilder builder = builderFactory.newDocumentBuilder();
426                 Document contextDoc = builder.parse( contextFile );
427                 contextDoc.getDocumentElement().normalize();
428 
429                 NamedNodeMap nodeMap = contextDoc.getDocumentElement().getAttributes();
430                 Node reloadableAttribute = nodeMap.getNamedItem( "reloadable" );
431 
432                 reloadable = ( reloadableAttribute != null ) ? Boolean.valueOf( reloadableAttribute.getNodeValue() )
433                                                             : false;
434             }
435             getLog().debug( "context reloadable: " + reloadable );
436         }
437         catch ( IOException ioe )
438         {
439             getLog().error( "Could not parse file: [" + contextFile.getAbsolutePath() + "]", ioe );
440         }
441         catch ( ParserConfigurationException pce )
442         {
443             getLog().error( "Could not configure XML parser", pce );
444         }
445         catch ( SAXException se )
446         {
447             getLog().error( "Could not parse file: [" + contextFile.getAbsolutePath() + "]", se );
448         }
449 
450         return reloadable;
451     }
452 
453 
454     /**
455      * Gets the webapp directory to run.
456      * 
457      * @return the webapp directory
458      */
459     protected abstract File getDocBase();
460 
461     /**
462      * Gets the Tomcat context XML file to use.
463      * 
464      * @return the context XML file
465      */
466     protected abstract File getContextFile() throws MojoExecutionException;
467 
468     // ----------------------------------------------------------------------
469     // Private Methods
470     // ----------------------------------------------------------------------
471 
472     /**
473      * Gets whether this project uses WAR packaging.
474      * 
475      * @return whether this project uses WAR packaging
476      */
477     protected boolean isWar()
478     {
479         return "war".equals( packaging ) || ignorePackaging;
480     }
481 
482     /**
483      * Gets the URL of the running webapp.
484      * 
485      * @return the URL of the running webapp
486      * @throws MalformedURLException if the running webapp URL is invalid
487      */
488     private URL getWebappUrl()
489         throws MalformedURLException
490     {
491         return new URL( "http", "localhost", port, getPath() );
492     }
493 
494     /**
495      * Creates the Tomcat configuration directory with the necessary resources.
496      * 
497      * @throws IOException if the Tomcat configuration could not be created
498      * @throws MojoExecutionException if the Tomcat configuration could not be created
499      */
500     private void initConfiguration()
501         throws IOException, MojoExecutionException
502     {
503         if ( configurationDir.exists() )
504         {
505             getLog().info( getMessage( "AbstractRunMojo.usingConfiguration", configurationDir ) );
506         }
507         else
508         {
509             getLog().info( getMessage( "AbstractRunMojo.creatingConfiguration", configurationDir ) );
510 
511             configurationDir.mkdirs();
512 
513             File confDir = new File( configurationDir, "conf");
514             confDir.mkdir();
515 
516             copyFile("/conf/tomcat-users.xml", new File( confDir, "tomcat-users.xml" ) );
517             if ( tomcatWebXml != null )
518             {
519                 if ( !tomcatWebXml.exists() )
520                 {
521                     throw new MojoExecutionException( " tomcatWebXml " + tomcatWebXml.getPath() + " not exists" );
522                 }
523                 //MTOMCAT-42  here it's a real file resources not a one coming with the mojo 
524                 FileUtils.copyFile( tomcatWebXml, new File( confDir, "web.xml" ) );
525                 //copyFile( tomcatWebXml.getPath(), new File( confDir, "web.xml" ) );
526             }
527             else
528             {
529                 copyFile("/conf/web.xml", new File( confDir, "web.xml" ) );
530             }
531 
532             File logDir = new File( configurationDir, "logs" );
533             logDir.mkdir();
534 
535             File webappsDir = new File( configurationDir, "webapps" );
536             webappsDir.mkdir();
537 
538             if ( additionalConfigFilesDir != null && additionalConfigFilesDir.exists() )
539             {
540                 DirectoryScanner scanner = new DirectoryScanner();
541                 scanner.addDefaultExcludes();
542                 scanner.setBasedir( additionalConfigFilesDir.getPath() );
543                 scanner.scan();
544 
545                 String[] files = scanner.getIncludedFiles();
546 
547                 if ( files != null && files.length > 0 )
548                 {
549                     getLog().info( "Coping additional tomcat config files" );
550 
551                     for ( int i = 0; i < files.length; i++ )
552                     {
553                         File file = new File( additionalConfigFilesDir, files[i] );
554 
555                         getLog().info( " copy " + file.getName() );
556 
557                         FileUtils.copyFileToDirectory( file, confDir );
558                     }
559                 }
560             }
561         }
562     }
563 
564     /**
565      * Copies the specified class resource to the specified file.
566      * 
567      * @param fromPath the path of the class resource to copy
568      * @param toFile the file to copy to
569      * @throws IOException if the file could not be copied
570      */
571     private void copyFile( String fromPath, File toFile )
572         throws IOException
573     {
574         URL fromURL = getClass().getResource( fromPath );
575 
576         if ( fromURL == null )
577         {
578             throw new FileNotFoundException( fromPath );
579         }
580 
581         FileUtils.copyURLToFile( fromURL, toFile );
582     }
583 
584     /**
585      * Starts the embedded Tomcat server.
586      * 
587      * @throws IOException if the server could not be configured
588      * @throws LifecycleException if the server could not be started
589      * @throws MojoExecutionException if the server could not be configured
590      */
591     private void startContainer()
592         throws IOException, LifecycleException, MojoExecutionException
593     {
594         
595         // Set the system properties
596         setupSystemProperties();
597         final Embedded container;
598         if ( serverXml != null )
599         {
600             if ( !serverXml.exists() )
601             {
602                 throw new MojoExecutionException( serverXml.getPath() + " not exists" );
603             }
604 
605             container = new Catalina();
606             container.setCatalinaHome( configurationDir.getAbsolutePath() );
607             ( (Catalina) container).setConfigFile( serverXml.getPath() );
608             container.start();
609         }
610         else
611         {
612             // create server
613             container = new Embedded();
614             container.setCatalinaHome( configurationDir.getAbsolutePath() );
615             container.setRealm( new MemoryRealm() );
616             container.setUseNaming( useNaming );
617 
618             //container.createLoader( getTomcatClassLoader() ).
619 
620             // create context
621             Context context = createContext(container);
622 
623 
624             // create host
625             String appBase = new File( configurationDir, "webapps" ).getAbsolutePath();
626             Host host = container.createHost( "localHost", appBase );
627             
628             host.addChild( context );
629             
630             if ( addContextWarDependencies )
631             {
632                 Collection<Context> dependecyContexts = createDependencyContexts(container);
633                 for ( Context extraContext : dependecyContexts )
634                 {
635                     host.addChild( extraContext );
636                 }
637             }
638 
639             // create engine
640             Engine engine = container.createEngine();
641             engine.setName( "localEngine" );
642             engine.addChild( host );
643             engine.setDefaultHost( host.getName() );
644             container.addEngine( engine );
645             if ( useSeparateTomcatClassLoader )
646             {
647                 engine.setParentClassLoader( getTomcatClassLoader() );
648             }
649             // create http connector
650             Connector httpConnector = container.createConnector( (InetAddress) null, port, false );
651             if ( httpsPort > 0 )
652             {
653                 httpConnector.setRedirectPort( httpsPort );
654             }
655             httpConnector.setURIEncoding( uriEncoding );
656             container.addConnector( httpConnector );
657 
658             // create https connector
659             if ( httpsPort > 0 )
660             {
661                 Connector httpsConnector = container.createConnector( (InetAddress) null, httpsPort, true );
662                 if ( keystoreFile!=null)
663                 {
664                     httpsConnector.setAttribute("keystoreFile", keystoreFile);
665                 }
666                 if ( keystorePass!=null)
667                 {
668                     httpsConnector.setAttribute("keystorePass", keystorePass);
669                 }
670                 container.addConnector( httpsConnector );
671 
672             }
673 
674             // create ajp connector
675             if ( ajpPort > 0 )
676             {
677                 Connector ajpConnector = container.createConnector( (InetAddress) null, ajpPort, ajpProtocol );
678                 ajpConnector.setURIEncoding( uriEncoding );
679                 container.addConnector( ajpConnector );
680             }
681         }
682         container.start();
683         
684         EmbeddedRegistry.getInstance().register(container);
685     }
686 
687     protected ClassRealm getTomcatClassLoader()
688         throws MojoExecutionException
689     {
690         if ( this.tomcatRealm != null )
691         {
692             return tomcatRealm;
693         }
694         try
695         {
696             ClassWorld world = new ClassWorld();
697             ClassRealm root = world.newRealm( "tomcat", Thread.currentThread().getContextClassLoader() );
698 
699             for ( @SuppressWarnings("rawtypes")
700             Iterator i = pluginArtifacts.iterator(); i.hasNext(); )
701             {
702                 Artifact pluginArtifact = (Artifact) i.next();
703                 if ( "org.apache.tomcat".equals( pluginArtifact.getGroupId() ) )
704                 {
705                     if ( pluginArtifact.getFile() != null )
706                     {
707                         root.addURL( pluginArtifact.getFile().toURI().toURL() );
708                     }
709                 }
710             }
711             tomcatRealm = root;
712             return root;
713         }
714         catch ( DuplicateRealmException e )
715         {
716             throw new MojoExecutionException( e.getMessage(), e );
717         }
718         catch ( MalformedURLException e )
719         {
720             throw new MojoExecutionException( e.getMessage(), e );
721         }
722     }
723         
724     @SuppressWarnings( "unchecked" )
725     public Set<Artifact> getProjectArtifacts()
726     {
727         return project.getArtifacts();
728     }    
729 
730     /**
731      * Causes the current thread to wait indefinitely. This method does not return.
732      */
733     private void waitIndefinitely()
734     {
735         Object lock = new Object();
736 
737         synchronized ( lock )
738         {
739             try
740             {
741                 lock.wait();
742             }
743             catch ( InterruptedException exception )
744             {
745                 getLog().warn( getMessage( "AbstractRunMojo.interrupted" ), exception );
746             }
747         }
748     }
749     
750 
751     /**
752      * Set the SystemProperties from the configuration.
753      */
754     private void setupSystemProperties()
755     {
756         if ( systemProperties != null && !systemProperties.isEmpty() )
757         {
758             getLog().info( "setting SystemProperties:" );
759 
760             for ( String key : systemProperties.keySet() )
761             {
762                 String value = systemProperties.get( key );
763 
764                 if ( value != null )
765                 {
766                     getLog().info( " " + key + "=" + value );
767                     System.setProperty( key, value );
768                 }
769                 else
770                 {
771                     getLog().info( "skip sysProps " + key + " with empty value" );
772                 }
773             }
774         }
775     }
776     
777         
778     /**
779      * Allows the startup of additional webapps in the tomcat container by declaration with scope
780      * "tomcat".
781      * 
782      * @param container tomcat
783      * @return dependency tomcat contexts of warfiles in scope "tomcat"
784      */
785     private Collection<Context> createDependencyContexts( Embedded container ) throws MojoExecutionException
786     {
787         getLog().info( "Deploying dependency wars" );
788         // Let's add other modules
789         List<Context> contexts = new ArrayList<Context>();
790 
791         ScopeArtifactFilter filter = new ScopeArtifactFilter( "tomcat" );
792         @SuppressWarnings("unchecked")
793         Set<Artifact> artifacts = project.getArtifacts();
794         for ( Artifact artifact : artifacts )
795         {
796 
797             // Artifact is not yet registered and it has neither test, nor a
798             // provided scope, not is it optional
799             if ( "war".equals( artifact.getType() ) && !artifact.isOptional() && filter.include( artifact ) )
800             {
801                 getLog().info( "Deploy warfile: " + String.valueOf( artifact.getFile() ) );
802                 File webapps = new File( configurationDir, "webapps" );
803                 File artifactWarDir = new File( webapps, artifact.getArtifactId() );
804                 if ( !artifactWarDir.exists() )
805                 {
806                     //dont extract if exists
807                     artifactWarDir.mkdir();
808                     try
809                     {
810                         UnArchiver unArchiver = archiverManager.getUnArchiver( "zip" );
811                         unArchiver.setSourceFile( artifact.getFile() );
812                         unArchiver.setDestDirectory( artifactWarDir );
813 
814                         // Extract the module
815                         unArchiver.extract();
816                     }
817                     catch ( IOException e )
818                     {
819                         getLog().error( e );
820                         continue;
821                     }
822                     catch ( NoSuchArchiverException e )
823                     {
824                         getLog().error( e );
825                         continue;
826                     }
827                     catch ( ArchiverException e )
828                     {
829                         getLog().error( e );
830                         continue;
831                     }
832                 }
833                 WebappLoader webappLoader = new WebappLoader( Thread.currentThread().getContextClassLoader() );
834                 Context context = container.createContext( "/" + artifact.getArtifactId(), artifactWarDir.getAbsolutePath() );
835                 context.setLoader( webappLoader );
836                 File contextFile = getContextFile();
837                 if (contextFile != null)
838                 {
839                     context.setConfigFile( getContextFile().getAbsolutePath() );
840                 }
841                 contexts.add( context );
842                 
843             }
844         }
845         return contexts;
846     }
847 }