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