KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > catalina > startup > HostConfig


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18
19 package org.apache.catalina.startup;
20
21
22 import java.io.BufferedOutputStream JavaDoc;
23 import java.io.File JavaDoc;
24 import java.io.FileOutputStream JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.InputStream JavaDoc;
27 import java.util.ArrayList JavaDoc;
28 import java.util.HashMap JavaDoc;
29 import java.util.LinkedHashMap JavaDoc;
30 import java.util.jar.JarEntry JavaDoc;
31 import java.util.jar.JarFile JavaDoc;
32
33 import javax.management.ObjectName JavaDoc;
34
35 import org.apache.catalina.Container;
36 import org.apache.catalina.Context;
37 import org.apache.catalina.Engine;
38 import org.apache.catalina.Host;
39 import org.apache.catalina.Lifecycle;
40 import org.apache.catalina.LifecycleEvent;
41 import org.apache.catalina.LifecycleListener;
42 import org.apache.catalina.core.ContainerBase;
43 import org.apache.catalina.core.StandardHost;
44 import org.apache.catalina.util.StringManager;
45 import org.apache.tomcat.util.digester.Digester;
46 import org.apache.tomcat.util.modeler.Registry;
47
48
49 /**
50  * Startup event listener for a <b>Host</b> that configures the properties
51  * of that Host, and the associated defined contexts.
52  *
53  * @author Craig R. McClanahan
54  * @author Remy Maucherat
55  * @version $Revision: 467222 $ $Date: 2006-10-24 05:17:11 +0200 (mar., 24 oct. 2006) $
56  */

57 public class HostConfig
58     implements LifecycleListener {
59     
60     protected static org.apache.commons.logging.Log log=
61          org.apache.commons.logging.LogFactory.getLog( HostConfig.class );
62
63     // ----------------------------------------------------- Instance Variables
64

65
66     /**
67      * App base.
68      */

69     protected File JavaDoc appBase = null;
70
71
72     /**
73      * Config base.
74      */

75     protected File JavaDoc configBase = null;
76
77
78     /**
79      * The Java class name of the Context configuration class we should use.
80      */

81     protected String JavaDoc configClass = "org.apache.catalina.startup.ContextConfig";
82
83
84     /**
85      * The Java class name of the Context implementation we should use.
86      */

87     protected String JavaDoc contextClass = "org.apache.catalina.core.StandardContext";
88
89
90     /**
91      * The Host we are associated with.
92      */

93     protected Host host = null;
94
95     
96     /**
97      * The JMX ObjectName of this component.
98      */

99     protected ObjectName JavaDoc oname = null;
100     
101
102     /**
103      * The string resources for this package.
104      */

105     protected static final StringManager sm =
106         StringManager.getManager(Constants.Package);
107
108
109     /**
110      * Should we deploy XML Context config files?
111      */

112     protected boolean deployXML = false;
113
114
115     /**
116      * Should we unpack WAR files when auto-deploying applications in the
117      * <code>appBase</code> directory?
118      */

119     protected boolean unpackWARs = false;
120
121
122     /**
123      * Map of deployed applications.
124      */

125     protected HashMap JavaDoc deployed = new HashMap JavaDoc();
126
127     
128     /**
129      * List of applications which are being serviced, and shouldn't be
130      * deployed/undeployed/redeployed at the moment.
131      */

132     protected ArrayList JavaDoc serviced = new ArrayList JavaDoc();
133     
134
135     /**
136      * Attribute value used to turn on/off XML validation
137      */

138     protected boolean xmlValidation = false;
139
140
141     /**
142      * Attribute value used to turn on/off XML namespace awarenes.
143      */

144     protected boolean xmlNamespaceAware = false;
145
146
147     /**
148      * The <code>Digester</code> instance used to parse context descriptors.
149      */

150     protected static Digester digester = createDigester();
151
152
153     // ------------------------------------------------------------- Properties
154

155
156     /**
157      * Return the Context configuration class name.
158      */

159     public String JavaDoc getConfigClass() {
160
161         return (this.configClass);
162
163     }
164
165
166     /**
167      * Set the Context configuration class name.
168      *
169      * @param configClass The new Context configuration class name.
170      */

171     public void setConfigClass(String JavaDoc configClass) {
172
173         this.configClass = configClass;
174
175     }
176
177
178     /**
179      * Return the Context implementation class name.
180      */

181     public String JavaDoc getContextClass() {
182
183         return (this.contextClass);
184
185     }
186
187
188     /**
189      * Set the Context implementation class name.
190      *
191      * @param contextClass The new Context implementation class name.
192      */

193     public void setContextClass(String JavaDoc contextClass) {
194
195         this.contextClass = contextClass;
196
197     }
198
199
200     /**
201      * Return the deploy XML config file flag for this component.
202      */

203     public boolean isDeployXML() {
204
205         return (this.deployXML);
206
207     }
208
209
210     /**
211      * Set the deploy XML config file flag for this component.
212      *
213      * @param deployXML The new deploy XML flag
214      */

215     public void setDeployXML(boolean deployXML) {
216
217         this.deployXML= deployXML;
218
219     }
220
221
222     /**
223      * Return the unpack WARs flag.
224      */

225     public boolean isUnpackWARs() {
226
227         return (this.unpackWARs);
228
229     }
230
231
232     /**
233      * Set the unpack WARs flag.
234      *
235      * @param unpackWARs The new unpack WARs flag
236      */

237     public void setUnpackWARs(boolean unpackWARs) {
238
239         this.unpackWARs = unpackWARs;
240
241     }
242     
243     
244      /**
245      * Set the validation feature of the XML parser used when
246      * parsing xml instances.
247      * @param xmlValidation true to enable xml instance validation
248      */

249     public void setXmlValidation(boolean xmlValidation){
250         this.xmlValidation = xmlValidation;
251     }
252
253     /**
254      * Get the server.xml <host> attribute's xmlValidation.
255      * @return true if validation is enabled.
256      *
257      */

258     public boolean getXmlValidation(){
259         return xmlValidation;
260     }
261
262     /**
263      * Get the server.xml <host> attribute's xmlNamespaceAware.
264      * @return true if namespace awarenes is enabled.
265      *
266      */

267     public boolean getXmlNamespaceAware(){
268         return xmlNamespaceAware;
269     }
270
271
272     /**
273      * Set the namespace aware feature of the XML parser used when
274      * parsing xml instances.
275      * @param xmlNamespaceAware true to enable namespace awareness
276      */

277     public void setXmlNamespaceAware(boolean xmlNamespaceAware){
278         this.xmlNamespaceAware=xmlNamespaceAware;
279     }
280
281
282     // --------------------------------------------------------- Public Methods
283

284
285     /**
286      * Process the START event for an associated Host.
287      *
288      * @param event The lifecycle event that has occurred
289      */

290     public void lifecycleEvent(LifecycleEvent event) {
291
292         if (event.getType().equals(Lifecycle.PERIODIC_EVENT))
293             check();
294
295         // Identify the host we are associated with
296
try {
297             host = (Host) event.getLifecycle();
298             if (host instanceof StandardHost) {
299                 setDeployXML(((StandardHost) host).isDeployXML());
300                 setUnpackWARs(((StandardHost) host).isUnpackWARs());
301                 setXmlNamespaceAware(((StandardHost) host).getXmlNamespaceAware());
302                 setXmlValidation(((StandardHost) host).getXmlValidation());
303             }
304         } catch (ClassCastException JavaDoc e) {
305             log.error(sm.getString("hostConfig.cce", event.getLifecycle()), e);
306             return;
307         }
308
309         // Process the event that has occurred
310
if (event.getType().equals(Lifecycle.START_EVENT))
311             start();
312         else if (event.getType().equals(Lifecycle.STOP_EVENT))
313             stop();
314
315     }
316
317     
318     /**
319      * Add a serviced application to the list.
320      */

321     public synchronized void addServiced(String JavaDoc name) {
322         serviced.add(name);
323     }
324     
325     
326     /**
327      * Is application serviced ?
328      * @return state of the application
329      */

330     public synchronized boolean isServiced(String JavaDoc name) {
331         return (serviced.contains(name));
332     }
333     
334
335     /**
336      * Removed a serviced application from the list.
337      */

338     public synchronized void removeServiced(String JavaDoc name) {
339         serviced.remove(name);
340     }
341
342     
343     /**
344      * Get the instant where an application was deployed.
345      * @return 0L if no application with that name is deployed, or the instant
346      * on which the application was deployed
347      */

348     public long getDeploymentTime(String JavaDoc name) {
349         DeployedApplication app = (DeployedApplication) deployed.get(name);
350         if (app == null) {
351             return 0L;
352         } else {
353             return app.timestamp;
354         }
355     }
356     
357     
358     /**
359      * Has the specified application been deployed? Note applications defined
360      * in server.xml will not have been deployed.
361      * @return <code>true</code> if the application has been deployed and
362      * <code>false</code> if the applciation has not been deployed or does not
363      * exist
364      */

365     public boolean isDeployed(String JavaDoc name) {
366         DeployedApplication app = (DeployedApplication) deployed.get(name);
367         if (app == null) {
368             return false;
369         } else {
370             return true;
371         }
372     }
373     
374     
375     // ------------------------------------------------------ Protected Methods
376

377     
378     /**
379      * Create the digester which will be used to parse context config files.
380      */

381     protected static Digester createDigester() {
382         Digester digester = new Digester();
383         digester.setValidating(false);
384         // Add object creation rule
385
digester.addObjectCreate("Context", "org.apache.catalina.core.StandardContext",
386             "className");
387         // Set the properties on that object (it doesn't matter if extra
388
// properties are set)
389
digester.addSetProperties("Context");
390         return (digester);
391     }
392     
393
394     /**
395      * Return a File object representing the "application root" directory
396      * for our associated Host.
397      */

398     protected File JavaDoc appBase() {
399
400         if (appBase != null) {
401             return appBase;
402         }
403
404         File JavaDoc file = new File JavaDoc(host.getAppBase());
405         if (!file.isAbsolute())
406             file = new File JavaDoc(System.getProperty("catalina.base"),
407                             host.getAppBase());
408         try {
409             appBase = file.getCanonicalFile();
410         } catch (IOException JavaDoc e) {
411             appBase = file;
412         }
413         return (appBase);
414
415     }
416
417
418     /**
419      * Return a File object representing the "configuration root" directory
420      * for our associated Host.
421      */

422     protected File JavaDoc configBase() {
423
424         if (configBase != null) {
425             return configBase;
426         }
427
428         File JavaDoc file = new File JavaDoc(System.getProperty("catalina.base"), "conf");
429         Container parent = host.getParent();
430         if ((parent != null) && (parent instanceof Engine)) {
431             file = new File JavaDoc(file, parent.getName());
432         }
433         file = new File JavaDoc(file, host.getName());
434         try {
435             configBase = file.getCanonicalFile();
436         } catch (IOException JavaDoc e) {
437             configBase = file;
438         }
439         return (configBase);
440
441     }
442
443     /**
444      * Get the name of the configBase.
445      * For use with JMX management.
446      */

447     public String JavaDoc getConfigBaseName() {
448         return configBase().getAbsolutePath();
449     }
450
451     /**
452      * Given a context path, get the config file name.
453      */

454     protected String JavaDoc getConfigFile(String JavaDoc path) {
455         String JavaDoc basename = null;
456         if (path.equals("")) {
457             basename = "ROOT";
458         } else {
459             basename = path.substring(1).replace('/', '#');
460         }
461         return (basename);
462     }
463
464     
465     /**
466      * Given a context path, get the config file name.
467      */

468     protected String JavaDoc getDocBase(String JavaDoc path) {
469         String JavaDoc basename = null;
470         if (path.equals("")) {
471             basename = "ROOT";
472         } else {
473             basename = path.substring(1);
474         }
475         return (basename);
476     }
477
478     
479     /**
480      * Deploy applications for any directories or WAR files that are found
481      * in our "application root" directory.
482      */

483     protected void deployApps() {
484
485         File JavaDoc appBase = appBase();
486         File JavaDoc configBase = configBase();
487         // Deploy XML descriptors from configBase
488
deployDescriptors(configBase, configBase.list());
489         // Deploy WARs, and loop if additional descriptors are found
490
deployWARs(appBase, appBase.list());
491         // Deploy expanded folders
492
deployDirectories(appBase, appBase.list());
493         
494     }
495
496
497     /**
498      * Deploy applications for any directories or WAR files that are found
499      * in our "application root" directory.
500      */

501     protected void deployApps(String JavaDoc name) {
502
503         File JavaDoc appBase = appBase();
504         File JavaDoc configBase = configBase();
505         String JavaDoc baseName = getConfigFile(name);
506         String JavaDoc docBase = getConfigFile(name);
507         
508         // Deploy XML descriptors from configBase
509
File JavaDoc xml = new File JavaDoc(configBase, baseName + ".xml");
510         if (xml.exists())
511             deployDescriptor(name, xml, baseName + ".xml");
512         // Deploy WARs, and loop if additional descriptors are found
513
File JavaDoc war = new File JavaDoc(appBase, docBase + ".war");
514         if (war.exists())
515             deployWAR(name, war, docBase + ".war");
516         // Deploy expanded folders
517
File JavaDoc dir = new File JavaDoc(appBase, docBase);
518         if (dir.exists())
519             deployDirectory(name, dir, docBase);
520         
521     }
522
523
524     /**
525      * Deploy XML context descriptors.
526      */

527     protected void deployDescriptors(File JavaDoc configBase, String JavaDoc[] files) {
528
529         if (files == null)
530             return;
531         
532         for (int i = 0; i < files.length; i++) {
533
534             if (files[i].equalsIgnoreCase("META-INF"))
535                 continue;
536             if (files[i].equalsIgnoreCase("WEB-INF"))
537                 continue;
538             File JavaDoc contextXml = new File JavaDoc(configBase, files[i]);
539             if (files[i].toLowerCase().endsWith(".xml")) {
540
541                 // Calculate the context path and make sure it is unique
542
String JavaDoc nameTmp = files[i].substring(0, files[i].length() - 4);
543                 String JavaDoc contextPath = "/" + nameTmp.replace('#', '/');
544                 if (nameTmp.equals("ROOT")) {
545                     contextPath = "";
546                 }
547
548                 if (isServiced(contextPath))
549                     continue;
550                 
551                 String JavaDoc file = files[i];
552
553                 deployDescriptor(contextPath, contextXml, file);
554                 
555             }
556
557         }
558
559     }
560
561
562     /**
563      * @param contextPath
564      * @param contextXml
565      * @param file
566      */

567     protected void deployDescriptor(String JavaDoc contextPath, File JavaDoc contextXml, String JavaDoc file) {
568         if (deploymentExists(contextPath)) {
569             return;
570         }
571         
572         DeployedApplication deployedApp = new DeployedApplication(contextPath);
573
574         // Assume this is a configuration descriptor and deploy it
575
if(log.isDebugEnabled()) {
576             log.debug(sm.getString("hostConfig.deployDescriptor", file));
577         }
578
579         Context context = null;
580         try {
581             synchronized (digester) {
582                 try {
583                     context = (Context) digester.parse(contextXml);
584                     if (context == null) {
585                         log.error(sm.getString("hostConfig.deployDescriptor.error",
586                                 file));
587                         return;
588                     }
589                 } finally {
590                     digester.reset();
591                 }
592             }
593             if (context instanceof Lifecycle) {
594                 Class JavaDoc clazz = Class.forName(host.getConfigClass());
595                 LifecycleListener listener =
596                     (LifecycleListener) clazz.newInstance();
597                 ((Lifecycle) context).addLifecycleListener(listener);
598             }
599             context.setConfigFile(contextXml.getAbsolutePath());
600             context.setPath(contextPath);
601             // Add the associated docBase to the redeployed list if it's a WAR
602
boolean isWar = false;
603             boolean isExternal = false;
604             if (context.getDocBase() != null) {
605                 File JavaDoc docBase = new File JavaDoc(context.getDocBase());
606                 if (!docBase.isAbsolute()) {
607                     docBase = new File JavaDoc(appBase(), context.getDocBase());
608                 }
609                 // If external docBase, register .xml as redeploy first
610
if (!docBase.getCanonicalPath().startsWith(appBase().getAbsolutePath())) {
611                     isExternal = true;
612                     deployedApp.redeployResources.put
613                         (contextXml.getAbsolutePath(), new Long JavaDoc(contextXml.lastModified()));
614                     deployedApp.redeployResources.put(docBase.getAbsolutePath(),
615                         new Long JavaDoc(docBase.lastModified()));
616                     if (docBase.getAbsolutePath().toLowerCase().endsWith(".war")) {
617                         isWar = true;
618                     }
619                 } else {
620                     log.warn(sm.getString("hostConfig.deployDescriptor.localDocBaseSpecified",
621                              docBase));
622                     // Ignore specified docBase
623
context.setDocBase(null);
624                 }
625             }
626             host.addChild(context);
627             // Get paths for WAR and expanded WAR in appBase
628
String JavaDoc name = null;
629             String JavaDoc path = context.getPath();
630             if (path.equals("")) {
631                 name = "ROOT";
632             } else {
633                 if (path.startsWith("/")) {
634                     name = path.substring(1);
635                 } else {
636                     name = path;
637                 }
638             }
639             File JavaDoc expandedDocBase = new File JavaDoc(name);
640             File JavaDoc warDocBase = new File JavaDoc(name + ".war");
641             if (!expandedDocBase.isAbsolute()) {
642                 expandedDocBase = new File JavaDoc(appBase(), name);
643                 warDocBase = new File JavaDoc(appBase(), name + ".war");
644             }
645             // Add the eventual unpacked WAR and all the resources which will be
646
// watched inside it
647
if (isWar && unpackWARs) {
648                 deployedApp.redeployResources.put(expandedDocBase.getAbsolutePath(),
649                         new Long JavaDoc(expandedDocBase.lastModified()));
650                 deployedApp.redeployResources.put
651                     (contextXml.getAbsolutePath(), new Long JavaDoc(contextXml.lastModified()));
652                 addWatchedResources(deployedApp, expandedDocBase.getAbsolutePath(), context);
653             } else {
654                 // Find an existing matching war and expanded folder
655
if (warDocBase.exists()) {
656                     deployedApp.redeployResources.put(warDocBase.getAbsolutePath(),
657                             new Long JavaDoc(warDocBase.lastModified()));
658                 }
659                 if (expandedDocBase.exists()) {
660                     deployedApp.redeployResources.put(expandedDocBase.getAbsolutePath(),
661                             new Long JavaDoc(expandedDocBase.lastModified()));
662                     addWatchedResources(deployedApp,
663                             expandedDocBase.getAbsolutePath(), context);
664                 } else {
665                     addWatchedResources(deployedApp, null, context);
666                 }
667                 // Add the context XML to the list of files which should trigger a redeployment
668
if (!isExternal) {
669                     deployedApp.redeployResources.put
670                         (contextXml.getAbsolutePath(), new Long JavaDoc(contextXml.lastModified()));
671                 }
672             }
673         } catch (Throwable JavaDoc t) {
674             log.error(sm.getString("hostConfig.deployDescriptor.error",
675                                    file), t);
676         }
677
678         if (context != null && host.findChild(context.getName()) != null) {
679             deployed.put(contextPath, deployedApp);
680         }
681     }
682
683
684     /**
685      * Deploy WAR files.
686      */

687     protected void deployWARs(File JavaDoc appBase, String JavaDoc[] files) {
688         
689         if (files == null)
690             return;
691         
692         for (int i = 0; i < files.length; i++) {
693             
694             if (files[i].equalsIgnoreCase("META-INF"))
695                 continue;
696             if (files[i].equalsIgnoreCase("WEB-INF"))
697                 continue;
698             File JavaDoc dir = new File JavaDoc(appBase, files[i]);
699             if (files[i].toLowerCase().endsWith(".war")) {
700                 
701                 // Calculate the context path and make sure it is unique
702
String JavaDoc contextPath = "/" + files[i];
703                 int period = contextPath.lastIndexOf(".");
704                 if (period >= 0)
705                     contextPath = contextPath.substring(0, period);
706                 if (contextPath.equals("/ROOT"))
707                     contextPath = "";
708                 
709                 if (isServiced(contextPath))
710                     continue;
711                 
712                 String JavaDoc file = files[i];
713                 
714                 deployWAR(contextPath, dir, file);
715                 
716             }
717             
718         }
719         
720     }
721
722
723     /**
724      * @param contextPath
725      * @param dir
726      * @param file
727      */

728     protected void deployWAR(String JavaDoc contextPath, File JavaDoc dir, String JavaDoc file) {
729         
730         if (deploymentExists(contextPath))
731             return;
732         
733         // Checking for a nested /META-INF/context.xml
734
JarFile JavaDoc jar = null;
735         JarEntry JavaDoc entry = null;
736         InputStream JavaDoc istream = null;
737         BufferedOutputStream JavaDoc ostream = null;
738         File JavaDoc xml = new File JavaDoc
739             (configBase, file.substring(0, file.lastIndexOf(".")) + ".xml");
740         if (deployXML && !xml.exists()) {
741             try {
742                 jar = new JarFile JavaDoc(dir);
743                 entry = jar.getJarEntry(Constants.ApplicationContextXml);
744                 if (entry != null) {
745                     istream = jar.getInputStream(entry);
746                     
747                     configBase.mkdirs();
748                     
749                     ostream =
750                         new BufferedOutputStream JavaDoc
751                         (new FileOutputStream JavaDoc(xml), 1024);
752                     byte buffer[] = new byte[1024];
753                     while (true) {
754                         int n = istream.read(buffer);
755                         if (n < 0) {
756                             break;
757                         }
758                         ostream.write(buffer, 0, n);
759                     }
760                     ostream.flush();
761                     ostream.close();
762                     ostream = null;
763                     istream.close();
764                     istream = null;
765                     entry = null;
766                     jar.close();
767                     jar = null;
768                 }
769             } catch (Exception JavaDoc e) {
770                 // Ignore and continue
771
if (ostream != null) {
772                     try {
773                         ostream.close();
774                     } catch (Throwable JavaDoc t) {
775                         ;
776                     }
777                     ostream = null;
778                 }
779                 if (istream != null) {
780                     try {
781                         istream.close();
782                     } catch (Throwable JavaDoc t) {
783                         ;
784                     }
785                     istream = null;
786                 }
787             } finally {
788                 entry = null;
789                 if (jar != null) {
790                     try {
791                         jar.close();
792                     } catch (Throwable JavaDoc t) {
793                         ;
794                     }
795                     jar = null;
796                 }
797             }
798         }
799         
800         DeployedApplication deployedApp = new DeployedApplication(contextPath);
801         
802         // Deploy the application in this WAR file
803
if(log.isInfoEnabled())
804             log.info(sm.getString("hostConfig.deployJar", file));
805
806         // Populate redeploy resources with the WAR file
807
deployedApp.redeployResources.put
808             (dir.getAbsolutePath(), new Long JavaDoc(dir.lastModified()));
809
810         try {
811             Context context = (Context) Class.forName(contextClass).newInstance();
812             if (context instanceof Lifecycle) {
813                 Class JavaDoc clazz = Class.forName(host.getConfigClass());
814                 LifecycleListener listener =
815                     (LifecycleListener) clazz.newInstance();
816                 ((Lifecycle) context).addLifecycleListener(listener);
817             }
818             context.setPath(contextPath);
819             context.setDocBase(file);
820             if (xml.exists()) {
821                 context.setConfigFile(xml.getAbsolutePath());
822                 deployedApp.redeployResources.put
823                     (xml.getAbsolutePath(), new Long JavaDoc(xml.lastModified()));
824             }
825