1
17 package org.apache.catalina.startup;
18
19 import java.io.File;
20 import java.io.FileInputStream;
21 import java.io.FileOutputStream;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.OutputStream;
25 import java.net.MalformedURLException;
26 import java.net.URL;
27 import java.nio.file.Files;
28 import java.security.CodeSource;
29 import java.security.Permission;
30 import java.security.PermissionCollection;
31 import java.security.Policy;
32 import java.security.cert.Certificate;
33 import java.util.ArrayList;
34 import java.util.HashMap;
35 import java.util.HashSet;
36 import java.util.Iterator;
37 import java.util.LinkedHashMap;
38 import java.util.List;
39 import java.util.Locale;
40 import java.util.Map;
41 import java.util.Set;
42 import java.util.SortedSet;
43 import java.util.TreeSet;
44 import java.util.concurrent.ConcurrentHashMap;
45 import java.util.concurrent.ExecutorService;
46 import java.util.concurrent.Future;
47 import java.util.jar.JarEntry;
48 import java.util.jar.JarFile;
49 import java.util.regex.Matcher;
50 import java.util.regex.Pattern;
51
52 import javax.management.ObjectName;
53
54 import org.apache.catalina.Container;
55 import org.apache.catalina.Context;
56 import org.apache.catalina.DistributedManager;
57 import org.apache.catalina.Globals;
58 import org.apache.catalina.Host;
59 import org.apache.catalina.Lifecycle;
60 import org.apache.catalina.LifecycleEvent;
61 import org.apache.catalina.LifecycleListener;
62 import org.apache.catalina.Manager;
63 import org.apache.catalina.core.StandardContext;
64 import org.apache.catalina.core.StandardHost;
65 import org.apache.catalina.security.DeployXmlPermission;
66 import org.apache.catalina.util.ContextName;
67 import org.apache.catalina.util.IOTools;
68 import org.apache.juli.logging.Log;
69 import org.apache.juli.logging.LogFactory;
70 import org.apache.tomcat.util.ExceptionUtils;
71 import org.apache.tomcat.util.buf.UriUtil;
72 import org.apache.tomcat.util.digester.Digester;
73 import org.apache.tomcat.util.modeler.Registry;
74 import org.apache.tomcat.util.res.StringManager;
75
76
83 public class HostConfig implements LifecycleListener {
84
85 private static final Log log = LogFactory.getLog(HostConfig.class);
86
87
90 protected static final StringManager sm = StringManager.getManager(HostConfig.class);
91
92
95 protected static final long FILE_MODIFICATION_RESOLUTION_MS = 1000;
96
97
98
99
100
103 protected String contextClass = "org.apache.catalina.core.StandardContext";
104
105
106
109 protected Host host = null;
110
111
112
115 protected ObjectName oname = null;
116
117
118
122 protected boolean deployXML = false;
123
124
125
130 protected boolean copyXML = false;
131
132
133
137 protected boolean unpackWARs = false;
138
139
140
143 protected final Map<String, DeployedApplication> deployed =
144 new ConcurrentHashMap<>();
145
146
147
151 protected final ArrayList<String> serviced = new ArrayList<>();
152
153
154
157 protected Digester digester = createDigester(contextClass);
158 private final Object digesterLock = new Object();
159
160
164 protected final Set<String> invalidWars = new HashSet<>();
165
166
167
168
169
172 public String getContextClass() {
173 return this.contextClass;
174 }
175
176
177
182 public void setContextClass(String contextClass) {
183
184 String oldContextClass = this.contextClass;
185 this.contextClass = contextClass;
186
187 if (!oldContextClass.equals(contextClass)) {
188 synchronized (digesterLock) {
189 digester = createDigester(getContextClass());
190 }
191 }
192 }
193
194
195
198 public boolean isDeployXML() {
199 return this.deployXML;
200 }
201
202
203
208 public void setDeployXML(boolean deployXML) {
209 this.deployXML = deployXML;
210 }
211
212
213 private boolean isDeployThisXML(File docBase, ContextName cn) {
214 boolean deployThisXML = isDeployXML();
215 if (Globals.IS_SECURITY_ENABLED && !deployThisXML) {
216
217
218 Policy currentPolicy = Policy.getPolicy();
219 if (currentPolicy != null) {
220 URL contextRootUrl;
221 try {
222 contextRootUrl = docBase.toURI().toURL();
223 CodeSource cs = new CodeSource(contextRootUrl, (Certificate[]) null);
224 PermissionCollection pc = currentPolicy.getPermissions(cs);
225 Permission p = new DeployXmlPermission(cn.getBaseName());
226 if (pc.implies(p)) {
227 deployThisXML = true;
228 }
229 } catch (MalformedURLException e) {
230
231 log.warn(sm.getString("hostConfig.docBaseUrlInvalid"), e);
232 }
233 }
234 }
235
236 return deployThisXML;
237 }
238
239
240
243 public boolean isCopyXML() {
244 return this.copyXML;
245 }
246
247
248
253 public void setCopyXML(boolean copyXML) {
254
255 this.copyXML= copyXML;
256
257 }
258
259
260
263 public boolean isUnpackWARs() {
264 return this.unpackWARs;
265 }
266
267
268
273 public void setUnpackWARs(boolean unpackWARs) {
274 this.unpackWARs = unpackWARs;
275 }
276
277
278
279
280
281
286 @Override
287 public void lifecycleEvent(LifecycleEvent event) {
288
289
290 try {
291 host = (Host) event.getLifecycle();
292 if (host instanceof StandardHost) {
293 setCopyXML(((StandardHost) host).isCopyXML());
294 setDeployXML(((StandardHost) host).isDeployXML());
295 setUnpackWARs(((StandardHost) host).isUnpackWARs());
296 setContextClass(((StandardHost) host).getContextClass());
297 }
298 } catch (ClassCastException e) {
299 log.error(sm.getString("hostConfig.cce", event.getLifecycle()), e);
300 return;
301 }
302
303
304 if (event.getType().equals(Lifecycle.PERIODIC_EVENT)) {
305 check();
306 } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
307 beforeStart();
308 } else if (event.getType().equals(Lifecycle.START_EVENT)) {
309 start();
310 } else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
311 stop();
312 }
313 }
314
315
316
320 public synchronized void addServiced(String name) {
321 serviced.add(name);
322 }
323
324
325
330 public synchronized boolean isServiced(String name) {
331 return serviced.contains(name);
332 }
333
334
335
339 public synchronized void removeServiced(String name) {
340 serviced.remove(name);
341 }
342
343
344
350 public long getDeploymentTime(String name) {
351 DeployedApplication app = deployed.get(name);
352 if (app == null) {
353 return 0L;
354 }
355
356 return app.timestamp;
357 }
358
359
360
368 public boolean isDeployed(String name) {
369 return deployed.containsKey(name);
370 }
371
372
373
374
375
376
382 protected static Digester createDigester(String contextClassName) {
383 Digester digester = new Digester();
384 digester.setValidating(false);
385
386 digester.addObjectCreate("Context", contextClassName, "className");
387
388
389 digester.addSetProperties("Context");
390 return digester;
391 }
392
393 protected File returnCanonicalPath(String path) {
394 File file = new File(path);
395 if (!file.isAbsolute())
396 file = new File(host.getCatalinaBase(), path);
397 try {
398 return file.getCanonicalFile();
399 } catch (IOException e) {
400 return file;
401 }
402 }
403
404
405
410 public String getConfigBaseName() {
411 return host.getConfigBaseFile().getAbsolutePath();
412 }
413
414
415
419 protected void deployApps() {
420
421 File appBase = host.getAppBaseFile();
422 File configBase = host.getConfigBaseFile();
423 String[] filteredAppPaths = filterAppPaths(appBase.list());
424
425 deployDescriptors(configBase, configBase.list());
426
427 deployWARs(appBase, filteredAppPaths);
428
429 deployDirectories(appBase, filteredAppPaths);
430
431 }
432
433
434
442 protected String[] filterAppPaths(String[] unfilteredAppPaths) {
443 Pattern filter = host.getDeployIgnorePattern();
444 if (filter == null || unfilteredAppPaths == null) {
445 return unfilteredAppPaths;
446 }
447
448 List<String> filteredList = new ArrayList<>();
449 Matcher matcher = null;
450 for (String appPath : unfilteredAppPaths) {
451 if (matcher == null) {
452 matcher = filter.matcher(appPath);
453 } else {
454 matcher.reset(appPath);
455 }
456 if (matcher.matches()) {
457 if (log.isDebugEnabled()) {
458 log.debug(sm.getString("hostConfig.ignorePath", appPath));
459 }
460 } else {
461 filteredList.add(appPath);
462 }
463 }
464 return filteredList.toArray(new String[filteredList.size()]);
465 }
466
467
468
473 protected void deployApps(String name) {
474
475 File appBase = host.getAppBaseFile();
476 File configBase = host.getConfigBaseFile();
477 ContextName cn = new ContextName(name, false);
478 String baseName = cn.getBaseName();
479
480 if (deploymentExists(cn.getName())) {
481 return;
482 }
483
484
485 File xml = new File(configBase, baseName + ".xml");
486 if (xml.exists()) {
487 deployDescriptor(cn, xml);
488 return;
489 }
490
491 File war = new File(appBase, baseName + ".war");
492 if (war.exists()) {
493 deployWAR(cn, war);
494 return;
495 }
496
497 File dir = new File(appBase, baseName);
498 if (dir.exists())
499 deployDirectory(cn, dir);
500 }
501
502
503
508 protected void deployDescriptors(File configBase, String[] files) {
509
510 if (files == null)
511 return;
512
513 ExecutorService es = host.getStartStopExecutor();
514 List<Future<?>> results = new ArrayList<>();
515
516 for (int i = 0; i < files.length; i++) {
517 File contextXml = new File(configBase, files[i]);
518
519 if (files[i].toLowerCase(Locale.ENGLISH).endsWith(".xml")) {
520 ContextName cn = new ContextName(files[i], true);
521
522 if (isServiced(cn.getName()) || deploymentExists(cn.getName()))
523 continue;
524
525 results.add(
526 es.submit(new DeployDescriptor(this, cn, contextXml)));
527 }
528 }
529
530 for (Future<?> result : results) {
531 try {
532 result.get();
533 } catch (Exception e) {
534 log.error(sm.getString(
535 "hostConfig.deployDescriptor.threaded.error"), e);
536 }
537 }
538 }
539
540
541
546 @SuppressWarnings("null")
547 protected void deployDescriptor(ContextName cn, File contextXml) {
548
549 DeployedApplication deployedApp =
550 new DeployedApplication(cn.getName(), true);
551
552 long startTime = 0;
553
554 if(log.isInfoEnabled()) {
555 startTime = System.currentTimeMillis();
556 log.info(sm.getString("hostConfig.deployDescriptor",
557 contextXml.getAbsolutePath()));
558 }
559
560 Context context = null;
561 boolean isExternalWar = false;
562 boolean isExternal = false;
563 File expandedDocBase = null;
564
565 try (FileInputStream fis = new FileInputStream(contextXml)) {
566 synchronized (digesterLock) {
567 try {
568 context = (Context) digester.parse(fis);
569 } catch (Exception e) {
570 log.error(sm.getString(
571 "hostConfig.deployDescriptor.error",
572 contextXml.getAbsolutePath()), e);
573 } finally {
574 digester.reset();
575 if (context == null) {
576 context = new FailedContext();
577 }
578 }
579 }
580
581 if (context.getPath() != null) {
582 log.warn(sm.getString("hostConfig.deployDescriptor.path", context.getPath(),
583 contextXml.getAbsolutePath()));
584 }
585
586 Class<?> clazz = Class.forName(host.getConfigClass());
587 LifecycleListener listener = (LifecycleListener) clazz.getConstructor().newInstance();
588 context.addLifecycleListener(listener);
589
590 context.setConfigFile(contextXml.toURI().toURL());
591 context.setName(cn.getName());
592 context.setPath(cn.getPath());
593 context.setWebappVersion(cn.getVersion());
594
595 if (context.getDocBase() != null) {
596 File docBase = new File(context.getDocBase());
597 if (!docBase.isAbsolute()) {
598 docBase = new File(host.getAppBaseFile(), context.getDocBase());
599 }
600
601 if (!docBase.getCanonicalPath().startsWith(
602 host.getAppBaseFile().getAbsolutePath() + File.separator)) {
603 isExternal = true;
604 deployedApp.redeployResources.put(
605 contextXml.getAbsolutePath(),
606 Long.valueOf(contextXml.lastModified()));
607 deployedApp.redeployResources.put(docBase.getAbsolutePath(),
608 Long.valueOf(docBase.lastModified()));
609 if (docBase.getAbsolutePath().toLowerCase(Locale.ENGLISH).endsWith(".war")) {
610 isExternalWar = true;
611 }
612
613 File war = new File(host.getAppBaseFile(), cn.getBaseName() + ".war");
614 if (war.exists()) {
615 log.warn(sm.getString("hostConfig.deployDescriptor.hiddenWar",
616 contextXml.getAbsolutePath(), war.getAbsolutePath()));
617 }
618 File dir = new File(host.getAppBaseFile(), cn.getBaseName());
619 if (dir.exists()) {
620 log.warn(sm.getString("hostConfig.deployDescriptor.hiddenDir",
621 contextXml.getAbsolutePath(), dir.getAbsolutePath()));
622 }
623 } else {
624 log.warn(sm.getString("hostConfig.deployDescriptor.localDocBaseSpecified",
625 docBase));
626
627 context.setDocBase(null);
628 }
629 }
630
631 host.addChild(context);
632 } catch (Throwable t) {
633 ExceptionUtils.handleThrowable(t);
634 log.error(sm.getString("hostConfig.deployDescriptor.error",
635 contextXml.getAbsolutePath()), t);
636 } finally {
637
638
639
640 expandedDocBase = new File(host.getAppBaseFile(), cn.getBaseName());
641 if (context.getDocBase() != null
642 && !context.getDocBase().toLowerCase(Locale.ENGLISH).endsWith(".war")) {
643
644 expandedDocBase = new File(context.getDocBase());
645 if (!expandedDocBase.isAbsolute()) {
646
647 expandedDocBase = new File(host.getAppBaseFile(), context.getDocBase());
648 }
649 }
650
651 boolean unpackWAR = unpackWARs;
652 if (unpackWAR && context instanceof StandardContext) {
653 unpackWAR = ((StandardContext) context).getUnpackWAR();
654 }
655
656
657
658 if (isExternalWar) {
659 if (unpackWAR) {
660 deployedApp.redeployResources.put(expandedDocBase.getAbsolutePath(),
661 Long.valueOf(expandedDocBase.lastModified()));
662 addWatchedResources(deployedApp, expandedDocBase.getAbsolutePath(), context);
663 } else {
664 addWatchedResources(deployedApp, null, context);
665 }
666 } else {
667
668 if (!isExternal) {
669 File warDocBase = new File(expandedDocBase.getAbsolutePath() + ".war");
670 if (warDocBase.exists()) {
671 deployedApp.redeployResources.put(warDocBase.getAbsolutePath(),
672 Long.valueOf(warDocBase.lastModified()));
673 } else {
674
675 deployedApp.redeployResources.put(
676 warDocBase.getAbsolutePath(),
677 Long.valueOf(0));
678 }
679 }
680 if (unpackWAR) {
681 deployedApp.redeployResources.put(expandedDocBase.getAbsolutePath(),
682 Long.valueOf(expandedDocBase.lastModified()));
683 addWatchedResources(deployedApp,
684 expandedDocBase.getAbsolutePath(), context);
685 } else {
686 addWatchedResources(deployedApp, null, context);
687 }
688 if (!isExternal) {
689
690
691 deployedApp.redeployResources.put(
692 contextXml.getAbsolutePath(),
693 Long.valueOf(contextXml.lastModified()));
694 }
695 }
696
697
698 addGlobalRedeployResources(deployedApp);
699 }
700
701 if (host.findChild(context.getName()) != null) {
702 deployed.put(context.getName(), deployedApp);
703 }
704
705 if (log.isInfoEnabled()) {
706 log.info(sm.getString("hostConfig.deployDescriptor.finished",
707 contextXml.getAbsolutePath(), Long.valueOf(System.currentTimeMillis() - startTime)));
708 }
709 }
710
711
712
717 protected void deployWARs(File appBase, String[] files) {
718
719 if (files == null)
720 return;
721
722 ExecutorService es = host.getStartStopExecutor();
723 List<Future<?>> results = new ArrayList<>();
724
725 for (int i = 0; i < files.length; i++) {
726
727 if (files[i].equalsIgnoreCase("META-INF"))
728 continue;
729 if (files[i].equalsIgnoreCase("WEB-INF"))
730 continue;
731 File war = new File(appBase, files[i]);
732 if (files[i].toLowerCase(Locale.ENGLISH).endsWith(".war") &&
733 war.isFile() && !invalidWars.contains(files[i]) ) {
734
735 ContextName cn = new ContextName(files[i], true);
736
737 if (isServiced(cn.getName())) {
738 continue;
739 }
740 if (deploymentExists(cn.getName())) {
741 DeployedApplication app = deployed.get(cn.getName());
742 boolean unpackWAR = unpackWARs;
743 if (unpackWAR && host.findChild(cn.getName()) instanceof StandardContext) {
744 unpackWAR = ((StandardContext) host.findChild(cn.getName())).getUnpackWAR();
745 }
746 if (!unpackWAR && app != null) {
747
748
749 File dir = new File(appBase, cn.getBaseName());
750 if (dir.exists()) {
751 if (!app.loggedDirWarning) {
752 log.warn(sm.getString(
753 "hostConfig.deployWar.hiddenDir",
754 dir.getAbsoluteFile(),
755 war.getAbsoluteFile()));
756 app.loggedDirWarning = true;
757 }
758 } else {
759 app.loggedDirWarning = false;
760 }
761 }
762 continue;
763 }
764
765
766 if (!validateContextPath(appBase, cn.getBaseName())) {
767 log.error(sm.getString(
768 "hostConfig.illegalWarName", files[i]));
769 invalidWars.add(files[i]);
770 continue;
771 }
772
773 results.add(es.submit(new DeployWar(this, cn, war)));
774 }
775 }
776
777 for (Future<?> result : results) {
778 try {
779 result.get();
780 } catch (Exception e) {
781 log.error(sm.getString(
782 "hostConfig.deployWar.threaded.error"), e);
783 }
784 }
785 }
786
787
788 private boolean validateContextPath(File appBase, String contextPath) {
789
790
791
792 StringBuilder docBase;
793 String canonicalDocBase = null;
794
795 try {
796 String canonicalAppBase = appBase.getCanonicalPath();
797 docBase = new StringBuilder(canonicalAppBase);
798 if (canonicalAppBase.endsWith(File.separator)) {
799 docBase.append(contextPath.substring(1).replace(
800 '/', File.separatorChar));
801 } else {
802 docBase.append(contextPath.replace('/', File.separatorChar));
803 }
804
805
806
807 canonicalDocBase =
808 (new File(docBase.toString())).getCanonicalPath();
809
810
811
812 if (canonicalDocBase.endsWith(File.separator)) {
813 docBase.append(File.separator);
814 }
815 } catch (IOException ioe) {
816 return false;
817 }
818
819
820
821 return canonicalDocBase.equals(docBase.toString());
822 }
823
824
829 protected void deployWAR(ContextName cn, File war) {
830
831 File xml = new File(host.getAppBaseFile(),
832 cn.getBaseName() + "/" + Constants.ApplicationContextXml);
833
834 File warTracker = new File(host.getAppBaseFile(), cn.getBaseName() + Constants.WarTracker);
835
836 boolean xmlInWar = false;
837 try (JarFile jar = new JarFile(war)) {
838 JarEntry entry = jar.getJarEntry(Constants.ApplicationContextXml);
839 if (entry != null) {
840 xmlInWar = true;
841 }
842 } catch (IOException e) {
843
844 }
845
846
847
848
849 boolean useXml = false;
850
851
852 if (xml.exists() && unpackWARs &&
853 (!warTracker.exists() || warTracker.lastModified() == war.lastModified())) {
854 useXml = true;
855 }
856
857 Context context = null;
858 boolean deployThisXML = isDeployThisXML(war, cn);
859
860 try {
861 if (deployThisXML && useXml && !copyXML) {
862 synchronized (digesterLock) {
863 try {
864 context = (Context) digester.parse(xml);
865 } catch (Exception e) {
866 log.error(sm.getString(
867 "hostConfig.deployDescriptor.error",
868 war.getAbsolutePath()), e);
869 } finally {
870 digester.reset();
871 if (context == null) {
872 context = new FailedContext();
873 }
874 }
875 }
876 context.setConfigFile(xml.toURI().toURL());
877 } else if (deployThisXML && xmlInWar) {
878 synchronized (digesterLock) {
879 try (JarFile jar = new JarFile(war)) {
880 JarEntry entry = jar.getJarEntry(Constants.ApplicationContextXml);
881 try (InputStream istream = jar.getInputStream(entry)) {
882 context = (Context) digester.parse(istream);
883 }
884 } catch (Exception e) {
885 log.error(sm.getString(
886 "hostConfig.deployDescriptor.error",
887 war.getAbsolutePath()), e);
888 } finally {
889 digester.reset();
890 if (context == null) {
891 context = new FailedContext();
892 }
893 context.setConfigFile(
894 UriUtil.buildJarUrl(war, Constants.ApplicationContextXml));
895 }
896 }
897 } else if (!deployThisXML && xmlInWar) {
898
899
900 log.error(sm.getString("hostConfig.deployDescriptor.blocked",
901 cn.getPath(), Constants.ApplicationContextXml,
902 new File(host.getConfigBaseFile(), cn.getBaseName() + ".xml")));
903 } else {
904 context = (Context) Class.forName(contextClass).getConstructor().newInstance();
905 }
906 } catch (Throwable t) {
907 ExceptionUtils.handleThrowable(t);
908 log.error(sm.getString("hostConfig.deployWar.error",
909 war.getAbsolutePath()), t);
910 } finally {
911 if (context == null) {
912 context = new FailedContext();
913 }
914 }
915
916 boolean copyThisXml = false;
917 if (deployThisXML) {
918 if (host instanceof StandardHost) {
919 copyThisXml = ((StandardHost) host).isCopyXML();
920 }
921
922
923 if (!copyThisXml && context instanceof StandardContext) {
924 copyThisXml = ((StandardContext) context).getCopyXML();
925 }
926
927 if (xmlInWar && copyThisXml) {
928
929 xml = new File(host.getConfigBaseFile(),
930 cn.getBaseName() + ".xml");
931 try (JarFile jar = new JarFile(war)) {
932 JarEntry entry = jar.getJarEntry(Constants.ApplicationContextXml);
933 try (InputStream istream = jar.getInputStream(entry);
934 OutputStream ostream = new FileOutputStream(xml)) {
935 IOTools.flow(istream, ostream);
936 }
937 } catch (IOException e) {
938
939 }
940 }
941 }
942
943 DeployedApplication deployedApp = new DeployedApplication(cn.getName(),
944 xml.exists() && deployThisXML && copyThisXml);
945
946 long startTime = 0;
947
948 if(log.isInfoEnabled()) {
949 startTime = System.currentTimeMillis();
950 log.info(sm.getString("hostConfig.deployWar",
951 war.getAbsolutePath()));
952 }
953
954 try {
955
956 deployedApp.redeployResources.put
957 (war.getAbsolutePath(), Long.valueOf(war.lastModified()));
958
959 if (deployThisXML && xml.exists() && copyThisXml) {
960 deployedApp.redeployResources.put(xml.getAbsolutePath(),
961 Long.valueOf(xml.lastModified()));
962 } else {
963
964 deployedApp.redeployResources.put(
965 (new File(host.getConfigBaseFile(),
966 cn.getBaseName() + ".xml")).getAbsolutePath(),
967 Long.valueOf(0));
968 }
969
970 Class<?> clazz = Class.forName(host.getConfigClass());
971 LifecycleListener listener = (LifecycleListener) clazz.getConstructor().newInstance();
972 context.addLifecycleListener(listener);
973
974 context.setName(cn.getName());
975 context.setPath(cn.getPath());
976 context.setWebappVersion(cn.getVersion());
977 context.setDocBase(cn.getBaseName() + ".war");
978 host.addChild(context);
979 } catch (Throwable t) {
980 ExceptionUtils.handleThrowable(t);
981 log.error(sm.getString("hostConfig.deployWar.error",
982 war.getAbsolutePath()), t);
983 } finally {
984
985
986 boolean unpackWAR = unpackWARs;
987 if (unpackWAR && context instanceof StandardContext) {
988 unpackWAR = ((StandardContext) context).getUnpackWAR();
989 }
990 if (unpackWAR && context.getDocBase() != null) {
991 File docBase = new File(host.getAppBaseFile(), cn.getBaseName());
992 deployedApp.redeployResources.put(docBase.getAbsolutePath(),
993 Long.valueOf(docBase.lastModified()));
994 addWatchedResources(deployedApp, docBase.getAbsolutePath(),
995 context);
996 if (deployThisXML && !copyThisXml && (xmlInWar || xml.exists())) {
997 deployedApp.redeployResources.put(xml.getAbsolutePath(),
998 Long.valueOf(xml.lastModified()));
999 }
1000 } else {
1001
1002
1003 addWatchedResources(deployedApp, null, context);
1004 }
1005
1006
1007 addGlobalRedeployResources(deployedApp);
1008 }
1009
1010 deployed.put(cn.getName(), deployedApp);
1011
1012 if (log.isInfoEnabled()) {
1013 log.info(sm.getString("hostConfig.deployWar.finished",
1014 war.getAbsolutePath(), Long.valueOf(System.currentTimeMillis() - startTime)));
1015 }
1016 }
1017
1018
1019
1024 protected void deployDirectories(File appBase, String[] files) {
1025
1026 if (files == null)
1027 return;
1028
1029 ExecutorService es = host.getStartStopExecutor();
1030 List<Future<?>> results = new ArrayList<>();
1031
1032 for (int i = 0; i < files.length; i++) {
1033
1034 if (files[i].equalsIgnoreCase("META-INF"))
1035 continue;
1036 if (files[i].equalsIgnoreCase("WEB-INF"))
1037 continue;
1038 File dir = new File(appBase, files[i]);
1039 if (dir.isDirectory()) {
1040 ContextName cn = new ContextName(files[i], false);
1041
1042 if (isServiced(cn.getName()) || deploymentExists(cn.getName()))
1043 continue;
1044
1045 results.add(es.submit(new DeployDirectory(this, cn, dir)));
1046 }
1047 }
1048
1049 for (Future<?> result : results) {
1050 try {
1051 result.get();
1052 } catch (Exception e) {
1053 log.error(sm.getString(
1054 "hostConfig.deployDir.threaded.error"), e);
1055 }
1056 }
1057 }
1058
1059
1060
1065 protected void deployDirectory(ContextName cn, File dir) {
1066
1067
1068 long startTime = 0;
1069
1070 if( log.isInfoEnabled() ) {
1071 startTime = System.currentTimeMillis();
1072 log.info(sm.getString("hostConfig.deployDir",
1073 dir.getAbsolutePath()));
1074 }
1075
1076 Context context = null;
1077 File xml = new File(dir, Constants.ApplicationContextXml);
1078 File xmlCopy =
1079 new File(host.getConfigBaseFile(), cn.getBaseName() + ".xml");
1080
1081
1082 DeployedApplication deployedApp;
1083 boolean copyThisXml = isCopyXML();
1084 boolean deployThisXML = isDeployThisXML(dir, cn);
1085
1086 try {
1087 if (deployThisXML && xml.exists()) {
1088 synchronized (digesterLock) {
1089 try {
1090 context = (Context) digester.parse(xml);
1091 } catch (Exception e) {
1092 log.error(sm.getString(
1093 "hostConfig.deployDescriptor.error",
1094 xml), e);
1095 context = new FailedContext();
1096 } finally {
1097 digester.reset();
1098 if (context == null) {
1099 context = new FailedContext();
1100 }
1101 }
1102 }
1103
1104 if (copyThisXml == false && context instanceof StandardContext) {
1105
1106 copyThisXml = ((StandardContext) context).getCopyXML();
1107 }
1108
1109 if (copyThisXml) {
1110 Files.copy(xml.toPath(), xmlCopy.toPath());
1111 context.setConfigFile(xmlCopy.toURI().toURL());
1112 } else {
1113 context.setConfigFile(xml.toURI().toURL());
1114 }
1115 } else if (!deployThisXML && xml.exists()) {
1116
1117
1118 log.error(sm.getString("hostConfig.deployDescriptor.blocked",
1119 cn.getPath(), xml, xmlCopy));
1120 context = new FailedContext();
1121 } else {
1122 context = (Context) Class.forName(contextClass).getConstructor().newInstance();
1123 }
1124
1125 Class<?> clazz = Class.forName(host.getConfigClass());
1126 LifecycleListener listener = (LifecycleListener) clazz.getConstructor().newInstance();
1127 context.addLifecycleListener(listener);
1128
1129 context.setName(cn.getName());
1130 context.setPath(cn.getPath());
1131 context.setWebappVersion(cn.getVersion());
1132 context.setDocBase(cn.getBaseName());
1133 host.addChild(context);
1134 } catch (Throwable t) {
1135 ExceptionUtils.handleThrowable(t);
1136 log.error(sm.getString("hostConfig.deployDir.error",
1137 dir.getAbsolutePath()), t);
1138 } finally {
1139 deployedApp = new DeployedApplication(cn.getName(),
1140 xml.exists() && deployThisXML && copyThisXml);
1141
1142
1143
1144 deployedApp.redeployResources.put(dir.getAbsolutePath() + ".war",
1145 Long.valueOf(0));
1146 deployedApp.redeployResources.put(dir.getAbsolutePath(),
1147 Long.valueOf(dir.lastModified()));
1148 if (deployThisXML && xml.exists()) {
1149 if (copyThisXml) {
1150 deployedApp.redeployResources.put(
1151 xmlCopy.getAbsolutePath(),
1152 Long.valueOf(xmlCopy.lastModified()));
1153 } else {
1154 deployedApp.redeployResources.put(
1155 xml.getAbsolutePath(),
1156 Long.valueOf(xml.lastModified()));
1157
1158
1159 deployedApp.redeployResources.put(
1160 xmlCopy.getAbsolutePath(),
1161 Long.valueOf(0));
1162 }
1163 } else {
1164
1165
1166 deployedApp.redeployResources.put(
1167 xmlCopy.getAbsolutePath(),
1168 Long.valueOf(0));
1169 if (!xml.exists()) {
1170 deployedApp.redeployResources.put(
1171 xml.getAbsolutePath(),
1172 Long.valueOf(0));
1173 }
1174 }
1175 addWatchedResources(deployedApp, dir.getAbsolutePath(), context);
1176
1177
1178 addGlobalRedeployResources(deployedApp);
1179 }
1180
1181 deployed.put(cn.getName(), deployedApp);
1182
1183 if( log.isInfoEnabled() ) {
1184 log.info(sm.getString("hostConfig.deployDir.finished",
1185 dir.getAbsolutePath(), Long.valueOf(System.currentTimeMillis() - startTime)));
1186 }
1187 }
1188
1189
1190
1196 protected boolean deploymentExists(String contextName) {
1197 return (deployed.containsKey(contextName) ||
1198 (host.findChild(contextName) != null));
1199 }
1200
1201
1202
1208 protected void addWatchedResources(DeployedApplication app, String docBase,
1209 Context context) {
1210
1271 protected synchronized void checkResources(DeployedApplication app,
1272 boolean skipFileModificationResolutionCheck) {
1273 String[] resources =
1274 app.redeployResources.keySet().toArray(new String[0]);
1275
1276 long currentTimeWithResolutionOffset =
1277 System.currentTimeMillis() - FILE_MODIFICATION_RESOLUTION_MS;
1278 for (int i = 0; i < resources.length; i++) {
1279 File resource = new File(resources[i]);
1280 if (log.isDebugEnabled())
1281 log.debug("Checking context[" + app.name +
1282 "] redeploy resource " + resource);
1283 long lastModified =
1284 app.redeployResources.get(resources[i]).longValue();
1285 if (resource.exists() || lastModified == 0) {
1286
1287
1288
1289
1290 if (resource.lastModified() != lastModified && (!host.getAutoDeploy() ||
1291 resource.lastModified() < currentTimeWithResolutionOffset ||
1292 skipFileModificationResolutionCheck)) {
1293 if (resource.isDirectory()) {
1294
1295 app.redeployResources.put(resources[i],
1296 Long.valueOf(resource.lastModified()));
1297 } else if (app.hasDescriptor &&
1298 resource.getName().toLowerCase(
1299 Locale.ENGLISH).endsWith(".war")) {
1300
1301
1302
1303
1304 Context context = (Context) host.findChild(app.name);
1305 String docBase = context.getDocBase();
1306 if (!docBase.toLowerCase(Locale.ENGLISH).endsWith(".war")) {
1307
1308 File docBaseFile = new File(docBase);
1309 if (!docBaseFile.isAbsolute()) {
1310 docBaseFile = new File(host.getAppBaseFile(),
1311 docBase);
1312 }
1313 reload(app, docBaseFile, resource.getAbsolutePath());
1314 } else {
1315 reload(app, null, null);
1316 }
1317
1318 app.redeployResources.put(resources[i],
1319 Long.valueOf(resource.lastModified()));
1320 app.timestamp = System.currentTimeMillis();
1321 boolean unpackWAR = unpackWARs;
1322 if (unpackWAR && context instanceof StandardContext) {
1323 unpackWAR = ((StandardContext) context).getUnpackWAR();
1324 }
1325 if (unpackWAR) {
1326 addWatchedResources(app, context.getDocBase(), context);
1327 } else {
1328 addWatchedResources(app, null, context);
1329 }
1330 return;
1331 } else {
1332
1333
1334 undeploy(app);
1335 deleteRedeployResources(app, resources, i, false);
1336 return;
1337 }
1338 }
1339 } else {
1340
1341
1342 try {
1343 Thread.sleep(500);
1344 } catch (InterruptedException e1) {
1345
1346 }
1347
1348 if (resource.exists()) {
1349 continue;
1350 }
1351
1352 undeploy(app);
1353 deleteRedeployResources(app, resources, i, true);
1354 return;
1355 }
1356 }
1357 resources = app.reloadResources.keySet().toArray(new String[0]);
1358 boolean update = false;
1359 for (int i = 0; i < resources.length; i++) {
1360 File resource = new File(resources[i]);
1361 if (log.isDebugEnabled()) {
1362 log.debug("Checking context[" + app.name + "] reload resource " + resource);
1363 }
1364 long lastModified = app.reloadResources.get(resources[i]).longValue();
1365
1366
1367
1368
1369 if ((resource.lastModified() != lastModified &&
1370 (!host.getAutoDeploy() ||
1371 resource.lastModified() < currentTimeWithResolutionOffset ||
1372 skipFileModificationResolutionCheck)) ||
1373 update) {
1374 if (!update) {
1375
1376 reload(app, null, null);
1377 update = true;
1378 }
1379
1380
1381 app.reloadResources.put(resources[i],
1382 Long.valueOf(resource.lastModified()));
1383 }
1384 app.timestamp = System.currentTimeMillis();
1385 }
1386 }
1387
1388
1389
1393 private void reload(DeployedApplication app, File fileToRemove, String newDocBase) {
1394 if(log.isInfoEnabled())
1395 log.info(sm.getString("hostConfig.reload", app.name));
1396 Context context = (Context) host.findChild(app.name);
1397 if (context.getState().isAvailable()) {
1398 if (fileToRemove != null && newDocBase != null) {
1399 context.addLifecycleListener(
1400 new ExpandedDirectoryRemovalListener(fileToRemove, newDocBase));
1401 }
1402
1403 context.reload();
1404 } else {
1405
1406
1407 if (fileToRemove != null && newDocBase != null) {
1408 ExpandWar.delete(fileToRemove);
1409 context.setDocBase(newDocBase);
1410 }
1411 try {
1412 context.start();
1413 } catch (Exception e) {
1414 log.error(sm.getString("hostConfig.context.restart", app.name), e);
1415 }
1416 }
1417 }
1418
1419
1420 private void undeploy(DeployedApplication app) {
1421 if (log.isInfoEnabled())
1422 log.info(sm.getString("hostConfig.undeploy", app.name));
1423 Container context = host.findChild(app.name);
1424 try {
1425 host.removeChild(context);
1426 } catch (Throwable t) {
1427 ExceptionUtils.handleThrowable(t);
1428 log.warn(sm.getString
1429 ("hostConfig.context.remove", app.name), t);
1430 }
1431 deployed.remove(app.name);
1432 }
1433
1434
1435 private void deleteRedeployResources(DeployedApplication app, String[] resources, int i,
1436 boolean deleteReloadResources) {
1437
1438
1439 for (int j = i + 1; j < resources.length; j++) {
1440 File current = new File(resources[j]);
1441
1442 if (Constants.HostContextXml.equals(current.getName())) {
1443 continue;
1444 }
1445
1446
1447 if (isDeletableResource(app, current)) {
1448 if (log.isDebugEnabled()) {
1449 log.debug("Delete " + current);
1450 }
1451 ExpandWar.delete(current);
1452 }
1453 }
1454
1455
1456 if (deleteReloadResources) {
1457 String[] resources2 = app.reloadResources.keySet().toArray(new String[0]);
1458 for (int j = 0; j < resources2.length; j++) {
1459 File current = new File(resources2[j]);
1460
1461 if (Constants.HostContextXml.equals(current.getName())) {
1462 continue;
1463 }
1464
1465
1466 if (isDeletableResource(app, current)) {
1467 if (log.isDebugEnabled()) {
1468 log.debug("Delete " + current);
1469 }
1470 ExpandWar.delete(current);
1471 }
1472 }
1473 }
1474 }
1475
1476
1477
1484 private boolean isDeletableResource(DeployedApplication app, File resource) {
1485
1486
1487
1488
1489 if (!resource.isAbsolute()) {
1490 log.warn(sm.getString("hostConfig.resourceNotAbsolute", app.name, resource));
1491 return false;
1492 }
1493
1494
1495 String canonicalLocation;
1496 try {
1497 canonicalLocation = resource.getParentFile().getCanonicalPath();
1498 } catch (IOException e) {
1499 log.warn(sm.getString(
1500 "hostConfig.canonicalizing", resource.getParentFile(), app.name), e);
1501 return false;
1502 }
1503
1504 String canonicalAppBase;
1505 try {
1506 canonicalAppBase = host.getAppBaseFile().getCanonicalPath();
1507 } catch (IOException e) {
1508 log.warn(sm.getString(
1509 "hostConfig.canonicalizing", host.getAppBaseFile(), app.name), e);
1510 return false;
1511 }
1512
1513 if (canonicalLocation.equals(canonicalAppBase)) {
1514
1515 return true;
1516 }
1517
1518 String canonicalConfigBase;
1519 try {
1520 canonicalConfigBase = host.getConfigBaseFile().getCanonicalPath();
1521 } catch (IOException e) {
1522 log.warn(sm.getString(
1523 "hostConfig.canonicalizing", host.getConfigBaseFile(), app.name), e);
1524 return false;
1525 }
1526
1527 if (canonicalLocation.equals(canonicalConfigBase) &&
1528 resource.getName().endsWith(".xml")) {
1529
1530 return true;
1531 }
1532
1533
1534 return false;
1535 }
1536
1537
1538 public void beforeStart() {
1539 if (host.getCreateDirs()) {
1540 File[] dirs = new File[] {host.getAppBaseFile(),host.getConfigBaseFile()};
1541 for (int i=0; i<dirs.length; i++) {
1542 if (!dirs[i].mkdirs() && !dirs[i].isDirectory()) {
1543 log.error(sm.getString("hostConfig.createDirs",dirs[i]));
1544 }
1545 }
1546 }
1547 }
1548
1549
1550
1553 public void start() {
1554
1555 if (log.isDebugEnabled())
1556 log.debug(sm.getString("hostConfig.start"));
1557
1558 try {
1559 ObjectName hostON = host.getObjectName();
1560 oname = new ObjectName
1561 (hostON.getDomain() + ":type=Deployer,host=" + host.getName());
1562 Registry.getRegistry(null, null).registerComponent
1563 (this, oname, this.getClass().getName());
1564 } catch (Exception e) {
1565 log.warn(sm.getString("hostConfig.jmx.register", oname), e);
1566 }
1567
1568 if (!host.getAppBaseFile().isDirectory()) {
1569 log.error(sm.getString("hostConfig.appBase", host.getName(),
1570 host.getAppBaseFile().getPath()));
1571 host.setDeployOnStartup(false);
1572 host.setAutoDeploy(false);
1573 }
1574
1575 if (host.getDeployOnStartup())
1576 deployApps();
1577
1578 }
1579
1580
1581
1584 public void stop() {
1585
1586 if (log.isDebugEnabled())
1587 log.debug(sm.getString("hostConfig.stop"));
1588
1589 if (oname != null) {
1590 try {
1591 Registry.getRegistry(null, null).unregisterComponent(oname);
1592 } catch (Exception e) {
1593 log.warn(sm.getString("hostConfig.jmx.unregister", oname), e);
1594 }
1595 }
1596 oname = null;
1597 }
1598
1599
1600
1603 protected void check() {
1604
1605 if (host.getAutoDeploy()) {
1606
1607 DeployedApplication[] apps =
1608 deployed.values().toArray(new DeployedApplication[0]);
1609 for (int i = 0; i < apps.length; i++) {
1610 if (!isServiced(apps[i].name))
1611 checkResources(apps[i], false);
1612 }
1613
1614
1615 if (host.getUndeployOldVersions()) {
1616 checkUndeploy();
1617 }
1618
1619
1620 deployApps();
1621 }
1622 }
1623
1624
1625
1637 public void check(String name) {
1638 DeployedApplication app = deployed.get(name);
1639 if (app != null) {
1640 checkResources(app, true);
1641 }
1642 deployApps(name);
1643 }
1644
1645
1649 public synchronized void checkUndeploy() {
1650 if (deployed.size() < 2) {
1651 return;
1652 }
1653
1654
1655 SortedSet<String> sortedAppNames = new TreeSet<>();
1656 sortedAppNames.addAll(deployed.keySet());
1657
1658 Iterator<String> iter = sortedAppNames.iterator();
1659
1660 ContextName previous = new ContextName(iter.next(), false);
1661 do {
1662 ContextName current = new ContextName(iter.next(), false);
1663
1664 if (current.getPath().equals(previous.getPath())) {
1665
1666
1667 Context previousContext = (Context) host.findChild(previous.getName());
1668 Context currentContext = (Context) host.findChild(current.getName());
1669 if (previousContext != null && currentContext != null &&
1670 currentContext.getState().isAvailable() &&
1671 !isServiced(previous.getName())) {
1672 Manager manager = previousContext.getManager();
1673 if (manager != null) {
1674 int sessionCount;
1675 if (manager instanceof DistributedManager) {
1676 sessionCount = ((DistributedManager) manager).getActiveSessionsFull();
1677 } else {
1678 sessionCount = manager.getActiveSessions();
1679 }
1680 if (sessionCount == 0) {
1681 if (log.isInfoEnabled()) {
1682 log.info(sm.getString(
1683 "hostConfig.undeployVersion", previous.getName()));
1684 }
1685 DeployedApplication app = deployed.get(previous.getName());
1686 String[] resources = app.redeployResources.keySet().toArray(new String[0]);
1687
1688
1689
1690 undeploy(app);
1691 deleteRedeployResources(app, resources, -1, true);
1692 }
1693 }
1694 }
1695 }
1696 previous = current;
1697 } while (iter.hasNext());
1698 }
1699
1700
1705 public void manageApp(Context context) {
1706
1707 String contextName = context.getName();
1708
1709 if (deployed.containsKey(contextName))
1710 return;
1711
1712 DeployedApplication deployedApp =
1713 new DeployedApplication(contextName, false);
1714
1715
1716 boolean isWar = false;
1717 if (context.getDocBase() != null) {
1718 File docBase = new File(context.getDocBase());
1719 if (!docBase.isAbsolute()) {
1720 docBase = new File(host.getAppBaseFile(), context.getDocBase());
1721 }
1722 deployedApp.redeployResources.put(docBase.getAbsolutePath(),
1723 Long.valueOf(docBase.lastModified()));
1724 if (docBase.getAbsolutePath().toLowerCase(Locale.ENGLISH).endsWith(".war")) {
1725 isWar = true;
1726 }
1727 }
1728 host.addChild(context);
1729
1730
1731 boolean unpackWAR = unpackWARs;
1732 if (unpackWAR && context instanceof StandardContext) {
1733 unpackWAR = ((StandardContext) context).getUnpackWAR();
1734 }
1735 if (isWar && unpackWAR) {
1736 File docBase = new File(host.getAppBaseFile(), context.getBaseName());
1737 deployedApp.redeployResources.put(docBase.getAbsolutePath(),
1738 Long.valueOf(docBase.lastModified()));
1739 addWatchedResources(deployedApp, docBase.getAbsolutePath(), context);
1740 } else {
1741 addWatchedResources(deployedApp, null, context);
1742 }
1743 deployed.put(contextName, deployedApp);
1744 }
1745
1746
1751 public void unmanageApp(String contextName) {
1752 if(isServiced(contextName)) {
1753 deployed.remove(contextName);
1754 host.removeChild(host.findChild(contextName));
1755 }
1756 }
1757
1758
1759
1760
1761
1765 protected static class DeployedApplication {
1766 public DeployedApplication(String name, boolean hasDescriptor) {
1767 this.name = name;
1768 this.hasDescriptor = hasDescriptor;
1769 }
1770
1771
1775 public final String name;
1776
1777
1781 public final boolean hasDescriptor;
1782
1783
1790 public final LinkedHashMap<String, Long> redeployResources =
1791 new LinkedHashMap<>();
1792
1793
1800 public final HashMap<String, Long> reloadResources = new HashMap<>();
1801
1802
1805 public long timestamp = System.currentTimeMillis();
1806
1807
1813 public boolean loggedDirWarning = false;
1814 }
1815
1816 private static class DeployDescriptor implements Runnable {
1817
1818 private HostConfig config;
1819 private ContextName cn;
1820 private File descriptor;
1821
1822 public DeployDescriptor(HostConfig config, ContextName cn,
1823 File descriptor) {
1824 this.config = config;
1825 this.cn = cn;
1826 this.descriptor= descriptor;
1827 }
1828
1829 @Override
1830 public void run() {
1831 config.deployDescriptor(cn, descriptor);
1832 }
1833 }
1834
1835 private static class DeployWar implements Runnable {
1836
1837 private HostConfig config;
1838 private ContextName cn;
1839 private File war;
1840
1841 public DeployWar(HostConfig config, ContextName cn, File war) {
1842 this.config = config;
1843 this.cn = cn;
1844 this.war = war;
1845 }
1846
1847 @Override
1848 public void run() {
1849 config.deployWAR(cn, war);
1850 }
1851 }
1852
1853 private static class DeployDirectory implements Runnable {
1854
1855 private HostConfig config;
1856 private ContextName cn;
1857 private File dir;
1858
1859 public DeployDirectory(HostConfig config, ContextName cn, File dir) {
1860 this.config = config;
1861 this.cn = cn;
1862 this.dir = dir;
1863 }
1864
1865 @Override
1866 public void run() {
1867 config.deployDirectory(cn, dir);
1868 }
1869 }
1870
1871
1872
1884 private static class ExpandedDirectoryRemovalListener implements LifecycleListener {
1885
1886 private final File toDelete;
1887 private final String newDocBase;
1888
1889
1897 public ExpandedDirectoryRemovalListener(File toDelete, String newDocBase) {
1898 this.toDelete = toDelete;
1899 this.newDocBase = newDocBase;
1900 }
1901
1902 @Override
1903 public void lifecycleEvent(LifecycleEvent event) {
1904 if (Lifecycle.AFTER_STOP_EVENT.equals(event.getType())) {
1905
1906 Context context = (Context) event.getLifecycle();
1907
1908
1909 ExpandWar.delete(toDelete);
1910
1911
1912 context.setDocBase(newDocBase);
1913
1914
1915
1916 context.removeLifecycleListener(this);
1917 }
1918 }
1919 }
1920 }
1921