1 package org.apache.tomcat.maven.plugin.tomcat6;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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.connector.Connector;
28 import org.apache.catalina.loader.WebappLoader;
29 import org.apache.catalina.realm.MemoryRealm;
30 import org.apache.catalina.startup.Catalina;
31 import org.apache.catalina.startup.Embedded;
32 import org.apache.maven.artifact.Artifact;
33 import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
34 import org.apache.maven.plugin.MojoExecutionException;
35 import org.apache.maven.plugin.MojoFailureException;
36 import org.apache.maven.project.MavenProject;
37 import org.apache.tomcat.maven.common.run.EmbeddedRegistry;
38 import org.codehaus.plexus.archiver.ArchiverException;
39 import org.codehaus.plexus.archiver.UnArchiver;
40 import org.codehaus.plexus.archiver.manager.ArchiverManager;
41 import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
42 import org.codehaus.plexus.classworlds.ClassWorld;
43 import org.codehaus.plexus.classworlds.realm.ClassRealm;
44 import org.codehaus.plexus.classworlds.realm.DuplicateRealmException;
45 import org.codehaus.plexus.util.DirectoryScanner;
46 import org.codehaus.plexus.util.FileUtils;
47 import org.w3c.dom.Document;
48 import org.w3c.dom.NamedNodeMap;
49 import org.w3c.dom.Node;
50 import org.xml.sax.SAXException;
51
52 import javax.xml.parsers.DocumentBuilder;
53 import javax.xml.parsers.DocumentBuilderFactory;
54 import javax.xml.parsers.ParserConfigurationException;
55 import java.io.File;
56 import java.io.FileNotFoundException;
57 import java.io.IOException;
58 import java.net.InetAddress;
59 import java.net.MalformedURLException;
60 import java.net.URL;
61 import java.security.Principal;
62 import java.util.ArrayList;
63 import java.util.Collection;
64 import java.util.Iterator;
65 import java.util.List;
66 import java.util.Map;
67 import java.util.Set;
68
69
70
71
72
73
74
75
76 public abstract class AbstractRunMojo
77 extends AbstractI18NTomcat6Mojo
78 {
79
80
81
82
83
84
85
86
87
88
89
90 private String packaging;
91
92
93
94
95
96
97 private File configurationDir;
98
99
100
101
102
103
104 private int port;
105
106
107
108
109
110
111
112
113
114 private int ajpPort;
115
116
117
118
119
120
121
122
123
124 private String ajpProtocol;
125
126
127
128
129
130
131
132
133
134 private int httpsPort;
135
136
137
138
139
140
141
142 private String uriEncoding;
143
144
145
146
147
148
149
150 private Map<String, String> systemProperties;
151
152
153
154
155
156
157
158 private File additionalConfigFilesDir;
159
160
161
162
163
164
165
166 private File serverXml;
167
168
169
170
171
172
173
174 private File tomcatWebXml;
175
176
177
178
179
180
181
182
183 private boolean fork;
184
185
186
187
188
189
190
191
192
193
194
195
196
197 private boolean addContextWarDependencies;
198
199
200
201
202
203
204
205
206
207 protected MavenProject project;
208
209
210
211
212
213
214
215 private ArchiverManager archiverManager;
216
217
218
219
220
221
222
223 protected boolean useSeparateTomcatClassLoader;
224
225
226
227
228
229
230 @SuppressWarnings( "rawtypes" )
231 private List pluginArtifacts;
232
233
234
235
236
237
238
239 private boolean ignorePackaging;
240
241
242
243
244
245
246
247 private String keystoreFile;
248
249
250
251
252
253
254
255 private String keystorePass;
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274 private boolean useNaming;
275
276
277
278
279
280
281
282
283
284 protected boolean contextReloadable;
285
286
287
288
289
290
291
292 protected File contextFile;
293
294
295
296
297
298
299
300
301 private String protocol;
302
303
304
305
306
307
308 private File tomcatUsers;
309
310
311
312
313
314
315
316 private File managerWarPath;
317
318
319
320
321
322
323
324
325 private ClassRealm tomcatRealm;
326
327
328
329
330
331
332
333
334 public void execute()
335 throws MojoExecutionException, MojoFailureException
336 {
337
338 if ( !isWar() )
339 {
340 getLog().info( messagesProvider.getMessage( "AbstractRunMojo.nonWar" ) );
341 return;
342 }
343 ClassLoader originalClassLoaser = Thread.currentThread().getContextClassLoader();
344 try
345 {
346 if ( useSeparateTomcatClassLoader )
347 {
348 Thread.currentThread().setContextClassLoader( getTomcatClassLoader() );
349 }
350 getLog().info( messagesProvider.getMessage( "AbstractRunMojo.runningWar", getWebappUrl() ) );
351
352 initConfiguration();
353 startContainer();
354 if ( !fork )
355 {
356 waitIndefinitely();
357 }
358 }
359 catch ( LifecycleException exception )
360 {
361 throw new MojoExecutionException( messagesProvider.getMessage( "AbstractRunMojo.cannotStart" ), exception );
362 }
363 catch ( IOException exception )
364 {
365 throw new MojoExecutionException(
366 messagesProvider.getMessage( "AbstractRunMojo.cannotCreateConfiguration" ), exception );
367 }
368 finally
369 {
370 if ( useSeparateTomcatClassLoader )
371 {
372 Thread.currentThread().setContextClassLoader( originalClassLoaser );
373 }
374 }
375 }
376
377
378
379
380
381
382
383
384
385
386 protected String getPath()
387 {
388 return path;
389 }
390
391
392
393
394
395
396
397
398
399 protected Context createContext( Embedded container )
400 throws IOException, MojoExecutionException
401 {
402 String contextPath = getPath();
403 Context context =
404 container.createContext( "/".equals( contextPath ) ? "" : contextPath, getDocBase().getAbsolutePath() );
405
406 if ( useSeparateTomcatClassLoader )
407 {
408 context.setParentClassLoader( getTomcatClassLoader() );
409 }
410
411 context.setLoader( createWebappLoader() );
412 File contextFile = getContextFile();
413 if ( contextFile != null )
414 {
415 context.setConfigFile( getContextFile().getAbsolutePath() );
416 }
417 return context;
418 }
419
420
421
422
423
424
425
426
427 protected WebappLoader createWebappLoader()
428 throws IOException, MojoExecutionException
429 {
430 if ( useSeparateTomcatClassLoader )
431 {
432 return ( isContextReloadable() )
433 ? new ExternalRepositoriesReloadableWebappLoader( getTomcatClassLoader(), getLog() )
434 : new WebappLoader( getTomcatClassLoader() );
435 }
436
437 return ( isContextReloadable() )
438 ? new ExternalRepositoriesReloadableWebappLoader( Thread.currentThread().getContextClassLoader(), getLog() )
439 : new WebappLoader( Thread.currentThread().getContextClassLoader() );
440 }
441
442
443
444
445
446
447 protected boolean isContextReloadable()
448 throws MojoExecutionException
449 {
450 if ( contextReloadable )
451 {
452 return true;
453 }
454
455 boolean reloadable = false;
456 try
457 {
458 if ( contextFile != null && contextFile.exists() )
459 {
460 DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
461 DocumentBuilder builder = builderFactory.newDocumentBuilder();
462 Document contextDoc = builder.parse( contextFile );
463 contextDoc.getDocumentElement().normalize();
464
465 NamedNodeMap nodeMap = contextDoc.getDocumentElement().getAttributes();
466 Node reloadableAttribute = nodeMap.getNamedItem( "reloadable" );
467
468 reloadable =
469 ( reloadableAttribute != null ) ? Boolean.valueOf( reloadableAttribute.getNodeValue() ) : false;
470 }
471 getLog().debug( "context reloadable: " + reloadable );
472 }
473 catch ( IOException ioe )
474 {
475 getLog().error( "Could not parse file: [" + contextFile.getAbsolutePath() + "]", ioe );
476 }
477 catch ( ParserConfigurationException pce )
478 {
479 getLog().error( "Could not configure XML parser", pce );
480 }
481 catch ( SAXException se )
482 {
483 getLog().error( "Could not parse file: [" + contextFile.getAbsolutePath() + "]", se );
484 }
485
486 return reloadable;
487 }
488
489
490
491
492
493
494
495 protected abstract File getDocBase();
496
497
498
499
500
501
502 protected abstract File getContextFile()
503 throws MojoExecutionException;
504
505
506
507
508
509
510
511
512
513
514 protected boolean isWar()
515 {
516 return "war".equals( packaging ) || ignorePackaging;
517 }
518
519
520
521
522
523
524
525 private URL getWebappUrl()
526 throws MalformedURLException
527 {
528 return new URL( "http", "localhost", port, getPath() );
529 }
530
531
532
533
534
535
536
537 private void initConfiguration()
538 throws IOException, MojoExecutionException
539 {
540 if ( configurationDir.exists() )
541 {
542 getLog().info( messagesProvider.getMessage( "AbstractRunMojo.usingConfiguration", configurationDir ) );
543 }
544 else
545 {
546 getLog().info( messagesProvider.getMessage( "AbstractRunMojo.creatingConfiguration", configurationDir ) );
547
548 configurationDir.mkdirs();
549
550 File confDir = new File( configurationDir, "conf" );
551 confDir.mkdir();
552
553 copyFile( "/conf/tomcat-users.xml", new File( confDir, "tomcat-users.xml" ) );
554
555 if ( tomcatWebXml != null )
556 {
557 if ( !tomcatWebXml.exists() )
558 {
559 throw new MojoExecutionException( " tomcatWebXml " + tomcatWebXml.getPath() + " not exists" );
560 }
561
562 FileUtils.copyFile( tomcatWebXml, new File( confDir, "web.xml" ) );
563
564 }
565 else
566 {
567 copyFile( "/conf/web.xml", new File( confDir, "web.xml" ) );
568 }
569
570 File logDir = new File( configurationDir, "logs" );
571 logDir.mkdir();
572
573 File webappsDir = new File( configurationDir, "webapps" );
574 webappsDir.mkdir();
575 if ( managerWarPath != null && managerWarPath.exists() )
576 {
577 FileUtils.copyFileToDirectory( managerWarPath, webappsDir );
578 }
579
580 if ( additionalConfigFilesDir != null && additionalConfigFilesDir.exists() )
581 {
582 DirectoryScanner scanner = new DirectoryScanner();
583 scanner.addDefaultExcludes();
584 scanner.setBasedir( additionalConfigFilesDir.getPath() );
585 scanner.scan();
586
587 String[] files = scanner.getIncludedFiles();
588
589 if ( files != null && files.length > 0 )
590 {
591 getLog().info( "Coping additional tomcat config files" );
592
593 for ( int i = 0; i < files.length; i++ )
594 {
595 File file = new File( additionalConfigFilesDir, files[i] );
596
597 getLog().info( " copy " + file.getName() );
598
599 FileUtils.copyFileToDirectory( file, confDir );
600 }
601 }
602 }
603 }
604 }
605
606
607
608
609
610
611
612
613 private void copyFile( String fromPath, File toFile )
614 throws IOException
615 {
616 URL fromURL = getClass().getResource( fromPath );
617
618 if ( fromURL == null )
619 {
620 throw new FileNotFoundException( fromPath );
621 }
622
623 FileUtils.copyURLToFile( fromURL, toFile );
624 }
625
626
627
628
629
630
631
632
633 private void startContainer()
634 throws IOException, LifecycleException, MojoExecutionException
635 {
636 String previousCatalinaBase = System.getProperty( "catalina.base" );
637
638 try
639 {
640
641
642 setupSystemProperties();
643
644 System.setProperty( "catalina.base", configurationDir.getAbsolutePath() );
645 System.setProperty( "catalina.home", configurationDir.getAbsolutePath() );
646
647 File catalinaPolicy = new File( configurationDir, "conf/catalina.policy" );
648
649 if ( catalinaPolicy.exists() )
650 {
651
652 System.setProperty( "java.security.policy", catalinaPolicy.getAbsolutePath() );
653 }
654
655 final Embedded container;
656 if ( serverXml != null )
657 {
658 if ( !serverXml.exists() )
659 {
660 throw new MojoExecutionException( serverXml.getPath() + " not exists" );
661 }
662
663 container = new Catalina();
664 container.setCatalinaHome( configurationDir.getAbsolutePath() );
665 container.setCatalinaBase( configurationDir.getAbsolutePath() );
666 ( (Catalina) container ).setConfigFile( serverXml.getPath() );
667 ( (Catalina) container ).setRedirectStreams( true );
668 ( (Catalina) container ).setUseNaming( this.useNaming );
669
670 container.start();
671 }
672 else
673 {
674
675 container = new Embedded();
676 container.setCatalinaHome( configurationDir.getAbsolutePath() );
677 MemoryRealm memoryRealm = new MemoryRealm()
678 {
679 @Override
680 public Principal authenticate( String username, String credentials )
681 {
682 return super.authenticate( username,
683 credentials );
684 }
685 };
686
687 if ( tomcatUsers != null )
688 {
689 if ( !tomcatUsers.exists() )
690 {
691 throw new MojoExecutionException( " tomcatUsers " + tomcatUsers.getPath() + " not exists" );
692 }
693 getLog().info( "use tomcat-users.xml from " + tomcatUsers.getAbsolutePath() );
694 memoryRealm.setPathname( tomcatUsers.getAbsolutePath() );
695
696 }
697
698 container.setRealm( memoryRealm );
699 container.setUseNaming( useNaming );
700
701
702
703
704 Context context = createContext( container );
705
706
707 String appBase = new File( configurationDir, "webapps" ).getAbsolutePath();
708 Host host = container.createHost( "localHost", appBase );
709
710 host.addChild( context );
711
712 if ( addContextWarDependencies )
713 {
714 Collection<Context> dependecyContexts = createDependencyContexts( container );
715 for ( Context extraContext : dependecyContexts )
716 {
717 host.addChild( extraContext );
718 }
719 }
720
721
722 Engine engine = container.createEngine();
723 engine.setName( "localEngine" );
724 engine.addChild( host );
725 engine.setDefaultHost( host.getName() );
726 container.addEngine( engine );
727 if ( useSeparateTomcatClassLoader )
728 {
729 engine.setParentClassLoader( getTomcatClassLoader() );
730 }
731
732 Connector httpConnector = container.createConnector( (InetAddress) null, port, protocol );
733 if ( httpsPort > 0 )
734 {
735 httpConnector.setRedirectPort( httpsPort );
736 }
737 httpConnector.setURIEncoding( uriEncoding );
738 container.addConnector( httpConnector );
739
740
741 if ( httpsPort > 0 )
742 {
743 Connector httpsConnector = container.createConnector( (InetAddress) null, httpsPort, true );
744 httpsConnector.setSecure( true );
745 httpsConnector.setProperty( "SSLEnabled", "true" );
746
747 httpsConnector.setProperty( "sslProtocol", "TLS" );
748 if ( keystoreFile != null )
749 {
750 httpsConnector.setAttribute( "keystoreFile", keystoreFile );
751 }
752 if ( keystorePass != null )
753 {
754 httpsConnector.setAttribute( "keystorePass", keystorePass );
755 }
756 container.addConnector( httpsConnector );
757
758 }
759
760
761 if ( ajpPort > 0 )
762 {
763 Connector ajpConnector = container.createConnector( (InetAddress) null, ajpPort, ajpProtocol );
764 ajpConnector.setURIEncoding( uriEncoding );
765 container.addConnector( ajpConnector );
766 }
767 container.start();
768 }
769
770 EmbeddedRegistry.getInstance().register( container );
771 }
772 finally
773 {
774 if ( previousCatalinaBase != null )
775 {
776 System.setProperty( "catalina.base", previousCatalinaBase );
777 }
778 }
779 }
780
781 protected ClassRealm getTomcatClassLoader()
782 throws MojoExecutionException
783 {
784 if ( this.tomcatRealm != null )
785 {
786 return tomcatRealm;
787 }
788 try
789 {
790 ClassWorld world = new ClassWorld();
791 ClassRealm root = world.newRealm( "tomcat", Thread.currentThread().getContextClassLoader() );
792
793 for ( @SuppressWarnings( "rawtypes" ) Iterator i = pluginArtifacts.iterator(); i.hasNext(); )
794 {
795 Artifact pluginArtifact = (Artifact) i.next();
796 if ( "org.apache.tomcat".equals( pluginArtifact.getGroupId() ) )
797 {
798 if ( pluginArtifact.getFile() != null )
799 {
800 root.addURL( pluginArtifact.getFile().toURI().toURL() );
801 }
802 }
803 }
804 tomcatRealm = root;
805 return root;
806 }
807 catch ( DuplicateRealmException e )
808 {
809 throw new MojoExecutionException( e.getMessage(), e );
810 }
811 catch ( MalformedURLException e )
812 {
813 throw new MojoExecutionException( e.getMessage(), e );
814 }
815 }
816
817 @SuppressWarnings( "unchecked" )
818 public Set<Artifact> getProjectArtifacts()
819 {
820 return project.getArtifacts();
821 }
822
823
824
825
826 private void waitIndefinitely()
827 {
828 Object lock = new Object();
829
830 synchronized ( lock )
831 {
832 try
833 {
834 lock.wait();
835 }
836 catch ( InterruptedException exception )
837 {
838 getLog().warn( messagesProvider.getMessage( "AbstractRunMojo.interrupted" ), exception );
839 }
840 }
841 }
842
843
844
845
846
847 private void setupSystemProperties()
848 {
849 if ( systemProperties != null && !systemProperties.isEmpty() )
850 {
851 getLog().info( "setting SystemProperties:" );
852
853 for ( String key : systemProperties.keySet() )
854 {
855 String value = systemProperties.get( key );
856
857 if ( value != null )
858 {
859 getLog().info( " " + key + "=" + value );
860 System.setProperty( key, value );
861 }
862 else
863 {
864 getLog().info( "skip sysProps " + key + " with empty value" );
865 }
866 }
867 }
868 }
869
870
871
872
873
874
875
876
877
878 private Collection<Context> createDependencyContexts( Embedded container )
879 throws MojoExecutionException
880 {
881 getLog().info( "Deploying dependency wars" );
882
883 List<Context> contexts = new ArrayList<Context>();
884
885 ScopeArtifactFilter filter = new ScopeArtifactFilter( "tomcat" );
886 @SuppressWarnings( "unchecked" ) Set<Artifact> artifacts = project.getArtifacts();
887 for ( Artifact artifact : artifacts )
888 {
889
890
891
892 if ( "war".equals( artifact.getType() ) && !artifact.isOptional() && filter.include( artifact ) )
893 {
894 getLog().info( "Deploy warfile: " + String.valueOf( artifact.getFile() ) );
895 File webapps = new File( configurationDir, "webapps" );
896 File artifactWarDir = new File( webapps, artifact.getArtifactId() );
897 if ( !artifactWarDir.exists() )
898 {
899
900 artifactWarDir.mkdir();
901 try
902 {
903 UnArchiver unArchiver = archiverManager.getUnArchiver( "zip" );
904 unArchiver.setSourceFile( artifact.getFile() );
905 unArchiver.setDestDirectory( artifactWarDir );
906
907
908 unArchiver.extract();
909 }
910 catch ( NoSuchArchiverException e )
911 {
912 getLog().error( e );
913 continue;
914 }
915 catch ( ArchiverException e )
916 {
917 getLog().error( e );
918 continue;
919 }
920 }
921 WebappLoader webappLoader = new WebappLoader( Thread.currentThread().getContextClassLoader() );
922 Context context =
923 container.createContext( "/" + artifact.getArtifactId(), artifactWarDir.getAbsolutePath() );
924 context.setLoader( webappLoader );
925 File contextFile = getContextFile();
926 if ( contextFile != null )
927 {
928 context.setConfigFile( getContextFile().getAbsolutePath() );
929 }
930 contexts.add( context );
931
932 }
933 }
934 return contexts;
935 }
936 }