001: /*
002: * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/core/StandardHost.java,v 1.29 2002/06/09 02:19:42 remm Exp $
003: * $Revision: 1.29 $
004: * $Date: 2002/06/09 02:19:42 $
005: *
006: * ====================================================================
007: *
008: * The Apache Software License, Version 1.1
009: *
010: * Copyright (c) 1999 The Apache Software Foundation. All rights
011: * reserved.
012: *
013: * Redistribution and use in source and binary forms, with or without
014: * modification, are permitted provided that the following conditions
015: * are met:
016: *
017: * 1. Redistributions of source code must retain the above copyright
018: * notice, this list of conditions and the following disclaimer.
019: *
020: * 2. Redistributions in binary form must reproduce the above copyright
021: * notice, this list of conditions and the following disclaimer in
022: * the documentation and/or other materials provided with the
023: * distribution.
024: *
025: * 3. The end-user documentation included with the redistribution, if
026: * any, must include the following acknowlegement:
027: * "This product includes software developed by the
028: * Apache Software Foundation (http://www.apache.org/)."
029: * Alternately, this acknowlegement may appear in the software itself,
030: * if and wherever such third-party acknowlegements normally appear.
031: *
032: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
033: * Foundation" must not be used to endorse or promote products derived
034: * from this software without prior written permission. For written
035: * permission, please contact apache@apache.org.
036: *
037: * 5. Products derived from this software may not be called "Apache"
038: * nor may "Apache" appear in their names without prior written
039: * permission of the Apache Group.
040: *
041: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
042: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
043: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
044: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
045: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
046: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
047: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
048: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
049: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
050: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
051: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
052: * SUCH DAMAGE.
053: * ====================================================================
054: *
055: * This software consists of voluntary contributions made by many
056: * individuals on behalf of the Apache Software Foundation. For more
057: * information on the Apache Software Foundation, please see
058: * <http://www.apache.org/>.
059: *
060: * [Additional notices, if required by prior licensing conditions]
061: *
062: */
063:
064: package org.apache.catalina.core;
065:
066: import java.io.IOException;
067: import java.net.JarURLConnection;
068: import java.net.URL;
069: import javax.servlet.ServletContext;
070: import javax.servlet.ServletException;
071: import javax.servlet.http.HttpServletRequest;
072: import javax.servlet.http.HttpServletResponse;
073: import org.apache.catalina.Container;
074: import org.apache.catalina.Context;
075: import org.apache.catalina.DefaultContext;
076: import org.apache.catalina.Deployer;
077: import org.apache.catalina.Globals;
078: import org.apache.catalina.HttpRequest;
079: import org.apache.catalina.Host;
080: import org.apache.catalina.Lifecycle;
081: import org.apache.catalina.LifecycleException;
082: import org.apache.catalina.LifecycleListener;
083: import org.apache.catalina.Request;
084: import org.apache.catalina.Response;
085: import org.apache.catalina.Valve;
086: import org.apache.catalina.valves.ErrorDispatcherValve;
087:
088: /**
089: * Standard implementation of the <b>Host</b> interface. Each
090: * child container must be a Context implementation to process the
091: * requests directed to a particular web application.
092: *
093: * @author Craig R. McClanahan
094: * @author Remy Maucherat
095: * @version $Revision: 1.29 $ $Date: 2002/06/09 02:19:42 $
096: */
097:
098: public class StandardHost extends ContainerBase implements Deployer,
099: Host {
100:
101: // ----------------------------------------------------------- Constructors
102:
103: /**
104: * Create a new StandardHost component with the default basic Valve.
105: */
106: public StandardHost() {
107:
108: super ();
109: pipeline.setBasic(new StandardHostValve());
110:
111: }
112:
113: // ----------------------------------------------------- Instance Variables
114:
115: /**
116: * The set of aliases for this Host.
117: */
118: private String[] aliases = new String[0];
119:
120: /**
121: * The application root for this Host.
122: */
123: private String appBase = ".";
124:
125: /**
126: * The auto deploy flag for this Host.
127: */
128: private boolean autoDeploy = true;
129:
130: /**
131: * The Java class name of the default context configuration class
132: * for deployed web applications.
133: */
134: private String configClass = "org.apache.catalina.startup.ContextConfig";
135:
136: /**
137: * The Java class name of the default Context implementation class for
138: * deployed web applications.
139: */
140: private String contextClass = "org.apache.catalina.core.StandardContext";
141:
142: /**
143: * The <code>Deployer</code> to whom we delegate application
144: * deployment requests.
145: */
146: private Deployer deployer = new StandardHostDeployer(this );
147:
148: /**
149: * deploy Context XML config files property.
150: */
151: private boolean deployXML = true;
152:
153: /**
154: * The Java class name of the default error reporter implementation class
155: * for deployed web applications.
156: */
157: private String errorReportValveClass = "org.apache.catalina.valves.ErrorReportValve";
158:
159: /**
160: * The descriptive information string for this implementation.
161: */
162: private static final String info = "org.apache.catalina.core.StandardHost/1.0";
163:
164: /**
165: * The live deploy flag for this Host.
166: */
167: private boolean liveDeploy = true;
168:
169: /**
170: * The Java class name of the default Mapper class for this Container.
171: */
172: private String mapperClass = "org.apache.catalina.core.StandardHostMapper";
173:
174: /**
175: * Unpack WARs property.
176: */
177: private boolean unpackWARs = true;
178:
179: /**
180: * Work Directory base for applications.
181: */
182: private String workDir = null;
183:
184: /**
185: * DefaultContext config
186: */
187: private DefaultContext defaultContext;
188:
189: // ------------------------------------------------------------- Properties
190:
191: /**
192: * Return the application root for this Host. This can be an absolute
193: * pathname, a relative pathname, or a URL.
194: */
195: public String getAppBase() {
196:
197: return (this .appBase);
198:
199: }
200:
201: /**
202: * Set the application root for this Host. This can be an absolute
203: * pathname, a relative pathname, or a URL.
204: *
205: * @param appBase The new application root
206: */
207: public void setAppBase(String appBase) {
208:
209: String oldAppBase = this .appBase;
210: this .appBase = appBase;
211: support.firePropertyChange("appBase", oldAppBase, this .appBase);
212:
213: }
214:
215: /**
216: * Return the value of the auto deploy flag. If true, it indicates that
217: * this host's child webapps should be discovred and automatically
218: * deployed at startup time.
219: */
220: public boolean getAutoDeploy() {
221:
222: return (this .autoDeploy);
223:
224: }
225:
226: /**
227: * Set the auto deploy flag value for this host.
228: *
229: * @param autoDeploy The new auto deploy flag
230: */
231: public void setAutoDeploy(boolean autoDeploy) {
232:
233: boolean oldAutoDeploy = this .autoDeploy;
234: this .autoDeploy = autoDeploy;
235: support.firePropertyChange("autoDeploy", oldAutoDeploy,
236: this .autoDeploy);
237:
238: }
239:
240: /**
241: * Return the Java class name of the context configuration class
242: * for new web applications.
243: */
244: public String getConfigClass() {
245:
246: return (this .configClass);
247:
248: }
249:
250: /**
251: * Set the Java class name of the context configuration class
252: * for new web applications.
253: *
254: * @param configClass The new context configuration class
255: */
256: public void setConfigClass(String configClass) {
257:
258: String oldConfigClass = this .configClass;
259: this .configClass = configClass;
260: support.firePropertyChange("configClass", oldConfigClass,
261: this .configClass);
262:
263: }
264:
265: /**
266: * Set the DefaultContext
267: * for new web applications.
268: *
269: * @param defaultContext The new DefaultContext
270: */
271: public void addDefaultContext(DefaultContext defaultContext) {
272:
273: DefaultContext oldDefaultContext = this .defaultContext;
274: this .defaultContext = defaultContext;
275: support.firePropertyChange("defaultContext", oldDefaultContext,
276: this .defaultContext);
277:
278: }
279:
280: /**
281: * Retrieve the DefaultContext for new web applications.
282: */
283: public DefaultContext getDefaultContext() {
284: return (this .defaultContext);
285: }
286:
287: /**
288: * Return the Java class name of the Context implementation class
289: * for new web applications.
290: */
291: public String getContextClass() {
292:
293: return (this .contextClass);
294:
295: }
296:
297: /**
298: * Set the Java class name of the Context implementation class
299: * for new web applications.
300: *
301: * @param contextClass The new context implementation class
302: */
303: public void setContextClass(String contextClass) {
304:
305: String oldContextClass = this .contextClass;
306: this .contextClass = contextClass;
307: support.firePropertyChange("contextClass", oldContextClass,
308: this .contextClass);
309:
310: }
311:
312: /**
313: * Deploy XML Context config files flag accessor.
314: */
315: public boolean isDeployXML() {
316:
317: return (deployXML);
318:
319: }
320:
321: /**
322: * Deploy XML Context config files flag mutator.
323: */
324: public void setDeployXML(boolean deployXML) {
325:
326: this .deployXML = deployXML;
327:
328: }
329:
330: /**
331: * Return the value of the live deploy flag. If true, it indicates that
332: * a background thread should be started that looks for web application
333: * context files, WAR files, or unpacked directories being dropped in to
334: * the <code>appBase</code> directory, and deploys new ones as they are
335: * encountered.
336: */
337: public boolean getLiveDeploy() {
338:
339: return (this .liveDeploy);
340:
341: }
342:
343: /**
344: * Set the live deploy flag value for this host.
345: *
346: * @param liveDeploy The new live deploy flag
347: */
348: public void setLiveDeploy(boolean liveDeploy) {
349:
350: boolean oldLiveDeploy = this .liveDeploy;
351: this .liveDeploy = liveDeploy;
352: support.firePropertyChange("liveDeploy", oldLiveDeploy,
353: this .liveDeploy);
354:
355: }
356:
357: /**
358: * Return the default Mapper class name.
359: */
360: public String getMapperClass() {
361:
362: return (this .mapperClass);
363:
364: }
365:
366: /**
367: * Set the default Mapper class name.
368: *
369: * @param mapperClass The new default Mapper class name
370: */
371: public void setMapperClass(String mapperClass) {
372:
373: String oldMapperClass = this .mapperClass;
374: this .mapperClass = mapperClass;
375: support.firePropertyChange("mapperClass", oldMapperClass,
376: this .mapperClass);
377:
378: }
379:
380: /**
381: * Return the Java class name of the error report valve class
382: * for new web applications.
383: */
384: public String getErrorReportValveClass() {
385:
386: return (this .errorReportValveClass);
387:
388: }
389:
390: /**
391: * Set the Java class name of the error report valve class
392: * for new web applications.
393: *
394: * @param errorReportValveClass The new error report valve class
395: */
396: public void setErrorReportValveClass(String errorReportValveClass) {
397:
398: String oldErrorReportValveClassClass = this .errorReportValveClass;
399: this .errorReportValveClass = errorReportValveClass;
400: support.firePropertyChange("errorReportValveClass",
401: oldErrorReportValveClassClass,
402: this .errorReportValveClass);
403:
404: }
405:
406: /**
407: * Return the canonical, fully qualified, name of the virtual host
408: * this Container represents.
409: */
410: public String getName() {
411:
412: return (name);
413:
414: }
415:
416: /**
417: * Set the canonical, fully qualified, name of the virtual host
418: * this Container represents.
419: *
420: * @param name Virtual host name
421: *
422: * @exception IllegalArgumentException if name is null
423: */
424: public void setName(String name) {
425:
426: if (name == null)
427: throw new IllegalArgumentException(sm
428: .getString("standardHost.nullName"));
429:
430: name = name.toLowerCase(); // Internally all names are lower case
431:
432: String oldName = this .name;
433: this .name = name;
434: support.firePropertyChange("name", oldName, this .name);
435:
436: }
437:
438: /**
439: * Unpack WARs flag accessor.
440: */
441: public boolean isUnpackWARs() {
442:
443: return (unpackWARs);
444:
445: }
446:
447: /**
448: * Unpack WARs flag mutator.
449: */
450: public void setUnpackWARs(boolean unpackWARs) {
451:
452: this .unpackWARs = unpackWARs;
453:
454: }
455:
456: /**
457: * Host work directory base.
458: */
459: public String getWorkDir() {
460:
461: return (workDir);
462: }
463:
464: /**
465: * Host work directory base.
466: */
467: public void setWorkDir(String workDir) {
468:
469: this .workDir = workDir;
470: }
471:
472: // --------------------------------------------------------- Public Methods
473:
474: /**
475: * Import the DefaultContext config into a web application context.
476: *
477: * @param context web application context to import default context
478: */
479: public void importDefaultContext(Context context) {
480:
481: if (this .defaultContext != null)
482: this .defaultContext.importDefaultContext(context);
483:
484: }
485:
486: /**
487: * Add an alias name that should be mapped to this same Host.
488: *
489: * @param alias The alias to be added
490: */
491: public void addAlias(String alias) {
492:
493: alias = alias.toLowerCase();
494:
495: // Skip duplicate aliases
496: for (int i = 0; i < aliases.length; i++) {
497: if (aliases[i].equals(alias))
498: return;
499: }
500:
501: // Add this alias to the list
502: String newAliases[] = new String[aliases.length + 1];
503: for (int i = 0; i < aliases.length; i++)
504: newAliases[i] = aliases[i];
505: newAliases[aliases.length] = alias;
506:
507: aliases = newAliases;
508:
509: // Inform interested listeners
510: fireContainerEvent(ADD_ALIAS_EVENT, alias);
511:
512: }
513:
514: /**
515: * Add a child Container, only if the proposed child is an implementation
516: * of Context.
517: *
518: * @param child Child container to be added
519: */
520: public void addChild(Container child) {
521:
522: if (!(child instanceof Context))
523: throw new IllegalArgumentException(sm
524: .getString("standardHost.notContext"));
525: super .addChild(child);
526:
527: }
528:
529: /**
530: * Return the set of alias names for this Host. If none are defined,
531: * a zero length array is returned.
532: */
533: public String[] findAliases() {
534:
535: return (this .aliases);
536:
537: }
538:
539: /**
540: * Return descriptive information about this Container implementation and
541: * the corresponding version number, in the format
542: * <code><description>/<version></code>.
543: */
544: public String getInfo() {
545:
546: return (info);
547:
548: }
549:
550: /**
551: * Return the Context that would be used to process the specified
552: * host-relative request URI, if any; otherwise return <code>null</code>.
553: *
554: * @param uri Request URI to be mapped
555: */
556: public Context map(String uri) {
557:
558: if (debug > 0)
559: log("Mapping request URI '" + uri + "'");
560: if (uri == null)
561: return (null);
562:
563: // Match on the longest possible context path prefix
564: if (debug > 1)
565: log(" Trying the longest context path prefix");
566: Context context = null;
567: String mapuri = uri;
568: while (true) {
569: context = (Context) findChild(mapuri);
570: if (context != null)
571: break;
572: int slash = mapuri.lastIndexOf('/');
573: if (slash < 0)
574: break;
575: mapuri = mapuri.substring(0, slash);
576: }
577:
578: // If no Context matches, select the default Context
579: if (context == null) {
580: if (debug > 1)
581: log(" Trying the default context");
582: context = (Context) findChild("");
583: }
584:
585: // Complain if no Context has been selected
586: if (context == null) {
587: log(sm.getString("standardHost.mappingError", uri));
588: return (null);
589: }
590:
591: // Return the mapped Context (if any)
592: if (debug > 0)
593: log(" Mapped to context '" + context.getPath() + "'");
594: return (context);
595:
596: }
597:
598: /**
599: * Remove the specified alias name from the aliases for this Host.
600: *
601: * @param alias Alias name to be removed
602: */
603: public void removeAlias(String alias) {
604:
605: alias = alias.toLowerCase();
606:
607: synchronized (aliases) {
608:
609: // Make sure this alias is currently present
610: int n = -1;
611: for (int i = 0; i < aliases.length; i++) {
612: if (aliases[i].equals(alias)) {
613: n = i;
614: break;
615: }
616: }
617: if (n < 0)
618: return;
619:
620: // Remove the specified alias
621: int j = 0;
622: String results[] = new String[aliases.length - 1];
623: for (int i = 0; i < aliases.length; i++) {
624: if (i != n)
625: results[j++] = aliases[i];
626: }
627: aliases = results;
628:
629: }
630:
631: // Inform interested listeners
632: fireContainerEvent(REMOVE_ALIAS_EVENT, alias);
633:
634: }
635:
636: /**
637: * Return a String representation of this component.
638: */
639: public String toString() {
640:
641: StringBuffer sb = new StringBuffer();
642: if (getParent() != null) {
643: sb.append(getParent().toString());
644: sb.append(".");
645: }
646: sb.append("StandardHost[");
647: sb.append(getName());
648: sb.append("]");
649: return (sb.toString());
650:
651: }
652:
653: /**
654: * Start this host.
655: *
656: * @exception LifecycleException if this component detects a fatal error
657: * that prevents it from being started
658: */
659: public synchronized void start() throws LifecycleException {
660:
661: // Set error report valve
662: if ((errorReportValveClass != null)
663: && (!errorReportValveClass.equals(""))) {
664: try {
665: Valve valve = (Valve) Class.forName(
666: errorReportValveClass).newInstance();
667: addValve(valve);
668: } catch (Throwable t) {
669: log(sm.getString(
670: "standardHost.invalidErrorReportValveClass",
671: errorReportValveClass));
672: }
673: }
674:
675: // Set dispatcher valve
676: addValve(new ErrorDispatcherValve());
677:
678: super .start();
679:
680: }
681:
682: // ------------------------------------------------------- Deployer Methods
683:
684: /**
685: * Install a new web application, whose web application archive is at the
686: * specified URL, into this container with the specified context path.
687: * A context path of "" (the empty string) should be used for the root
688: * application for this container. Otherwise, the context path must
689: * start with a slash.
690: * <p>
691: * If this application is successfully installed, a ContainerEvent of type
692: * <code>INSTALL_EVENT</code> will be sent to all registered listeners,
693: * with the newly created <code>Context</code> as an argument.
694: *
695: * @param contextPath The context path to which this application should
696: * be installed (must be unique)
697: * @param war A URL of type "jar:" that points to a WAR file, or type
698: * "file:" that points to an unpacked directory structure containing
699: * the web application to be installed
700: *
701: * @exception IllegalArgumentException if the specified context path
702: * is malformed (it must be "" or start with a slash)
703: * @exception IllegalStateException if the specified context path
704: * is already attached to an existing web application
705: * @exception IOException if an input/output error was encountered
706: * during install
707: */
708: public void install(String contextPath, URL war) throws IOException {
709:
710: deployer.install(contextPath, war);
711:
712: }
713:
714: /**
715: * <p>Install a new web application, whose context configuration file
716: * (consisting of a <code><Context></code> element) and web
717: * application archive are at the specified URLs.</p>
718: *
719: * <p>If this application is successfully installed, a ContainerEvent
720: * of type <code>INSTALL_EVENT</code> will be sent to all registered
721: * listeners, with the newly created <code>Context</code> as an argument.
722: * </p>
723: *
724: * @param config A URL that points to the context configuration file to
725: * be used for configuring the new Context
726: * @param war A URL of type "jar:" that points to a WAR file, or type
727: * "file:" that points to an unpacked directory structure containing
728: * the web application to be installed
729: *
730: * @exception IllegalArgumentException if one of the specified URLs is
731: * null
732: * @exception IllegalStateException if the context path specified in the
733: * context configuration file is already attached to an existing web
734: * application
735: * @exception IOException if an input/output error was encountered
736: * during installation
737: */
738: public synchronized void install(URL config, URL war)
739: throws IOException {
740:
741: deployer.install(config, war);
742:
743: }
744:
745: /**
746: * Return the Context for the deployed application that is associated
747: * with the specified context path (if any); otherwise return
748: * <code>null</code>.
749: *
750: * @param contextPath The context path of the requested web application
751: */
752: public Context findDeployedApp(String contextPath) {
753:
754: return (deployer.findDeployedApp(contextPath));
755:
756: }
757:
758: /**
759: * Return the context paths of all deployed web applications in this
760: * Container. If there are no deployed applications, a zero-length
761: * array is returned.
762: */
763: public String[] findDeployedApps() {
764:
765: return (deployer.findDeployedApps());
766:
767: }
768:
769: /**
770: * Remove an existing web application, attached to the specified context
771: * path. If this application is successfully removed, a
772: * ContainerEvent of type <code>REMOVE_EVENT</code> will be sent to all
773: * registered listeners, with the removed <code>Context</code> as
774: * an argument.
775: *
776: * @param contextPath The context path of the application to be removed
777: *
778: * @exception IllegalArgumentException if the specified context path
779: * is malformed (it must be "" or start with a slash)
780: * @exception IllegalArgumentException if the specified context path does
781: * not identify a currently installed web application
782: * @exception IOException if an input/output error occurs during
783: * removal
784: */
785: public void remove(String contextPath) throws IOException {
786:
787: deployer.remove(contextPath);
788:
789: }
790:
791: /**
792: * Start an existing web application, attached to the specified context
793: * path. Only starts a web application if it is not running.
794: *
795: * @param contextPath The context path of the application to be started
796: *
797: * @exception IllegalArgumentException if the specified context path
798: * is malformed (it must be "" or start with a slash)
799: * @exception IllegalArgumentException if the specified context path does
800: * not identify a currently installed web application
801: * @exception IOException if an input/output error occurs during
802: * startup
803: */
804: public void start(String contextPath) throws IOException {
805:
806: deployer.start(contextPath);
807:
808: }
809:
810: /**
811: * Stop an existing web application, attached to the specified context
812: * path. Only stops a web application if it is running.
813: *
814: * @param contextPath The context path of the application to be stopped
815: *
816: * @exception IllegalArgumentException if the specified context path
817: * is malformed (it must be "" or start with a slash)
818: * @exception IllegalArgumentException if the specified context path does
819: * not identify a currently installed web application
820: * @exception IOException if an input/output error occurs while stopping
821: * the web application
822: */
823: public void stop(String contextPath) throws IOException {
824:
825: deployer.stop(contextPath);
826:
827: }
828:
829: // ------------------------------------------------------ Protected Methods
830:
831: /**
832: * Add a default Mapper implementation if none have been configured
833: * explicitly.
834: *
835: * @param mapperClass Java class name of the default Mapper
836: */
837: protected void addDefaultMapper(String mapperClass) {
838:
839: super.addDefaultMapper(this.mapperClass);
840:
841: }
842:
843: }
|