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