001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.catalina.core;
019:
020: import javax.management.MBeanServer;
021: import javax.management.ObjectName;
022:
023: import org.apache.catalina.Container;
024: import org.apache.catalina.Context;
025: import org.apache.catalina.Host;
026: import org.apache.catalina.LifecycleException;
027: import org.apache.catalina.Valve;
028: import org.apache.catalina.startup.HostConfig;
029: import org.apache.catalina.valves.ValveBase;
030: import org.apache.tomcat.util.modeler.Registry;
031:
032: /**
033: * Standard implementation of the <b>Host</b> interface. Each
034: * child container must be a Context implementation to process the
035: * requests directed to a particular web application.
036: *
037: * @author Craig R. McClanahan
038: * @author Remy Maucherat
039: * @version $Revision: 497521 $ $Date: 2007-01-18 19:24:17 +0100 (jeu., 18 janv. 2007) $
040: */
041:
042: public class StandardHost extends ContainerBase implements Host {
043: /* Why do we implement deployer and delegate to deployer ??? */
044:
045: private static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory
046: .getLog(StandardHost.class);
047:
048: // ----------------------------------------------------------- Constructors
049:
050: /**
051: * Create a new StandardHost component with the default basic Valve.
052: */
053: public StandardHost() {
054:
055: super ();
056: pipeline.setBasic(new StandardHostValve());
057:
058: }
059:
060: // ----------------------------------------------------- Instance Variables
061:
062: /**
063: * The set of aliases for this Host.
064: */
065: private String[] aliases = new String[0];
066:
067: /**
068: * The application root for this Host.
069: */
070: private String appBase = ".";
071:
072: /**
073: * The auto deploy flag for this Host.
074: */
075: private boolean autoDeploy = true;
076:
077: /**
078: * The Java class name of the default context configuration class
079: * for deployed web applications.
080: */
081: private String configClass = "org.apache.catalina.startup.ContextConfig";
082:
083: /**
084: * The Java class name of the default Context implementation class for
085: * deployed web applications.
086: */
087: private String contextClass = "org.apache.catalina.core.StandardContext";
088:
089: /**
090: * The deploy on startup flag for this Host.
091: */
092: private boolean deployOnStartup = true;
093:
094: /**
095: * deploy Context XML config files property.
096: */
097: private boolean deployXML = true;
098:
099: /**
100: * The Java class name of the default error reporter implementation class
101: * for deployed web applications.
102: */
103: private String errorReportValveClass = "org.apache.catalina.valves.ErrorReportValve";
104:
105: /**
106: * The object name for the errorReportValve.
107: */
108: private ObjectName errorReportValveObjectName = null;
109:
110: /**
111: * The descriptive information string for this implementation.
112: */
113: private static final String info = "org.apache.catalina.core.StandardHost/1.0";
114:
115: /**
116: * The live deploy flag for this Host.
117: */
118: private boolean liveDeploy = true;
119:
120: /**
121: * Unpack WARs property.
122: */
123: private boolean unpackWARs = true;
124:
125: /**
126: * Work Directory base for applications.
127: */
128: private String workDir = null;
129:
130: /**
131: * Attribute value used to turn on/off XML validation
132: */
133: private boolean xmlValidation = false;
134:
135: /**
136: * Attribute value used to turn on/off XML namespace awarenes.
137: */
138: private boolean xmlNamespaceAware = false;
139:
140: // ------------------------------------------------------------- Properties
141:
142: /**
143: * Return the application root for this Host. This can be an absolute
144: * pathname, a relative pathname, or a URL.
145: */
146: public String getAppBase() {
147:
148: return (this .appBase);
149:
150: }
151:
152: /**
153: * Set the application root for this Host. This can be an absolute
154: * pathname, a relative pathname, or a URL.
155: *
156: * @param appBase The new application root
157: */
158: public void setAppBase(String appBase) {
159:
160: String oldAppBase = this .appBase;
161: this .appBase = appBase;
162: support.firePropertyChange("appBase", oldAppBase, this .appBase);
163:
164: }
165:
166: /**
167: * Return the value of the auto deploy flag. If true, it indicates that
168: * this host's child webapps will be dynamically deployed.
169: */
170: public boolean getAutoDeploy() {
171:
172: return (this .autoDeploy);
173:
174: }
175:
176: /**
177: * Set the auto deploy flag value for this host.
178: *
179: * @param autoDeploy The new auto deploy flag
180: */
181: public void setAutoDeploy(boolean autoDeploy) {
182:
183: boolean oldAutoDeploy = this .autoDeploy;
184: this .autoDeploy = autoDeploy;
185: support.firePropertyChange("autoDeploy", oldAutoDeploy,
186: this .autoDeploy);
187:
188: }
189:
190: /**
191: * Return the Java class name of the context configuration class
192: * for new web applications.
193: */
194: public String getConfigClass() {
195:
196: return (this .configClass);
197:
198: }
199:
200: /**
201: * Set the Java class name of the context configuration class
202: * for new web applications.
203: *
204: * @param configClass The new context configuration class
205: */
206: public void setConfigClass(String configClass) {
207:
208: String oldConfigClass = this .configClass;
209: this .configClass = configClass;
210: support.firePropertyChange("configClass", oldConfigClass,
211: this .configClass);
212:
213: }
214:
215: /**
216: * Return the Java class name of the Context implementation class
217: * for new web applications.
218: */
219: public String getContextClass() {
220:
221: return (this .contextClass);
222:
223: }
224:
225: /**
226: * Set the Java class name of the Context implementation class
227: * for new web applications.
228: *
229: * @param contextClass The new context implementation class
230: */
231: public void setContextClass(String contextClass) {
232:
233: String oldContextClass = this .contextClass;
234: this .contextClass = contextClass;
235: support.firePropertyChange("contextClass", oldContextClass,
236: this .contextClass);
237:
238: }
239:
240: /**
241: * Return the value of the deploy on startup flag. If true, it indicates
242: * that this host's child webapps should be discovred and automatically
243: * deployed at startup time.
244: */
245: public boolean getDeployOnStartup() {
246:
247: return (this .deployOnStartup);
248:
249: }
250:
251: /**
252: * Set the deploy on startup flag value for this host.
253: *
254: * @param deployOnStartup The new deploy on startup flag
255: */
256: public void setDeployOnStartup(boolean deployOnStartup) {
257:
258: boolean oldDeployOnStartup = this .deployOnStartup;
259: this .deployOnStartup = deployOnStartup;
260: support.firePropertyChange("deployOnStartup",
261: oldDeployOnStartup, this .deployOnStartup);
262:
263: }
264:
265: /**
266: * Deploy XML Context config files flag accessor.
267: */
268: public boolean isDeployXML() {
269:
270: return (deployXML);
271:
272: }
273:
274: /**
275: * Deploy XML Context config files flag mutator.
276: */
277: public void setDeployXML(boolean deployXML) {
278:
279: this .deployXML = deployXML;
280:
281: }
282:
283: /**
284: * Return the value of the live deploy flag. If true, it indicates that
285: * a background thread should be started that looks for web application
286: * context files, WAR files, or unpacked directories being dropped in to
287: * the <code>appBase</code> directory, and deploys new ones as they are
288: * encountered.
289: */
290: public boolean getLiveDeploy() {
291: return (this .autoDeploy);
292: }
293:
294: /**
295: * Set the live deploy flag value for this host.
296: *
297: * @param liveDeploy The new live deploy flag
298: */
299: public void setLiveDeploy(boolean liveDeploy) {
300: setAutoDeploy(liveDeploy);
301: }
302:
303: /**
304: * Return the Java class name of the error report valve class
305: * for new web applications.
306: */
307: public String getErrorReportValveClass() {
308:
309: return (this .errorReportValveClass);
310:
311: }
312:
313: /**
314: * Set the Java class name of the error report valve class
315: * for new web applications.
316: *
317: * @param errorReportValveClass The new error report valve class
318: */
319: public void setErrorReportValveClass(String errorReportValveClass) {
320:
321: String oldErrorReportValveClassClass = this .errorReportValveClass;
322: this .errorReportValveClass = errorReportValveClass;
323: support.firePropertyChange("errorReportValveClass",
324: oldErrorReportValveClassClass,
325: this .errorReportValveClass);
326:
327: }
328:
329: /**
330: * Return the canonical, fully qualified, name of the virtual host
331: * this Container represents.
332: */
333: public String getName() {
334:
335: return (name);
336:
337: }
338:
339: /**
340: * Set the canonical, fully qualified, name of the virtual host
341: * this Container represents.
342: *
343: * @param name Virtual host name
344: *
345: * @exception IllegalArgumentException if name is null
346: */
347: public void setName(String name) {
348:
349: if (name == null)
350: throw new IllegalArgumentException(sm
351: .getString("standardHost.nullName"));
352:
353: name = name.toLowerCase(); // Internally all names are lower case
354:
355: String oldName = this .name;
356: this .name = name;
357: support.firePropertyChange("name", oldName, this .name);
358:
359: }
360:
361: /**
362: * Unpack WARs flag accessor.
363: */
364: public boolean isUnpackWARs() {
365:
366: return (unpackWARs);
367:
368: }
369:
370: /**
371: * Unpack WARs flag mutator.
372: */
373: public void setUnpackWARs(boolean unpackWARs) {
374:
375: this .unpackWARs = unpackWARs;
376:
377: }
378:
379: /**
380: * Set the validation feature of the XML parser used when
381: * parsing xml instances.
382: * @param xmlValidation true to enable xml instance validation
383: */
384: public void setXmlValidation(boolean xmlValidation) {
385:
386: this .xmlValidation = xmlValidation;
387:
388: }
389:
390: /**
391: * Get the server.xml <host> attribute's xmlValidation.
392: * @return true if validation is enabled.
393: *
394: */
395: public boolean getXmlValidation() {
396: return xmlValidation;
397: }
398:
399: /**
400: * Get the server.xml <host> attribute's xmlNamespaceAware.
401: * @return true if namespace awarenes is enabled.
402: *
403: */
404: public boolean getXmlNamespaceAware() {
405: return xmlNamespaceAware;
406: }
407:
408: /**
409: * Set the namespace aware feature of the XML parser used when
410: * parsing xml instances.
411: * @param xmlNamespaceAware true to enable namespace awareness
412: */
413: public void setXmlNamespaceAware(boolean xmlNamespaceAware) {
414: this .xmlNamespaceAware = xmlNamespaceAware;
415: }
416:
417: /**
418: * Host work directory base.
419: */
420: public String getWorkDir() {
421:
422: return (workDir);
423: }
424:
425: /**
426: * Host work directory base.
427: */
428: public void setWorkDir(String workDir) {
429:
430: this .workDir = workDir;
431: }
432:
433: // --------------------------------------------------------- Public Methods
434:
435: /**
436: * Add an alias name that should be mapped to this same Host.
437: *
438: * @param alias The alias to be added
439: */
440: public void addAlias(String alias) {
441:
442: alias = alias.toLowerCase();
443:
444: // Skip duplicate aliases
445: for (int i = 0; i < aliases.length; i++) {
446: if (aliases[i].equals(alias))
447: return;
448: }
449:
450: // Add this alias to the list
451: String newAliases[] = new String[aliases.length + 1];
452: for (int i = 0; i < aliases.length; i++)
453: newAliases[i] = aliases[i];
454: newAliases[aliases.length] = alias;
455:
456: aliases = newAliases;
457:
458: // Inform interested listeners
459: fireContainerEvent(ADD_ALIAS_EVENT, alias);
460:
461: }
462:
463: /**
464: * Add a child Container, only if the proposed child is an implementation
465: * of Context.
466: *
467: * @param child Child container to be added
468: */
469: public void addChild(Container child) {
470:
471: if (!(child instanceof Context))
472: throw new IllegalArgumentException(sm
473: .getString("standardHost.notContext"));
474: super .addChild(child);
475:
476: }
477:
478: /**
479: * Return the set of alias names for this Host. If none are defined,
480: * a zero length array is returned.
481: */
482: public String[] findAliases() {
483:
484: return (this .aliases);
485:
486: }
487:
488: /**
489: * Return descriptive information about this Container implementation and
490: * the corresponding version number, in the format
491: * <code><description>/<version></code>.
492: */
493: public String getInfo() {
494:
495: return (info);
496:
497: }
498:
499: /**
500: * Return the Context that would be used to process the specified
501: * host-relative request URI, if any; otherwise return <code>null</code>.
502: *
503: * @param uri Request URI to be mapped
504: */
505: public Context map(String uri) {
506:
507: if (log.isDebugEnabled())
508: log.debug("Mapping request URI '" + uri + "'");
509: if (uri == null)
510: return (null);
511:
512: // Match on the longest possible context path prefix
513: if (log.isTraceEnabled())
514: log.trace(" Trying the longest context path prefix");
515: Context context = null;
516: String mapuri = uri;
517: while (true) {
518: context = (Context) findChild(mapuri);
519: if (context != null)
520: break;
521: int slash = mapuri.lastIndexOf('/');
522: if (slash < 0)
523: break;
524: mapuri = mapuri.substring(0, slash);
525: }
526:
527: // If no Context matches, select the default Context
528: if (context == null) {
529: if (log.isTraceEnabled())
530: log.trace(" Trying the default context");
531: context = (Context) findChild("");
532: }
533:
534: // Complain if no Context has been selected
535: if (context == null) {
536: log.error(sm.getString("standardHost.mappingError", uri));
537: return (null);
538: }
539:
540: // Return the mapped Context (if any)
541: if (log.isDebugEnabled())
542: log.debug(" Mapped to context '" + context.getPath() + "'");
543: return (context);
544:
545: }
546:
547: /**
548: * Remove the specified alias name from the aliases for this Host.
549: *
550: * @param alias Alias name to be removed
551: */
552: public void removeAlias(String alias) {
553:
554: alias = alias.toLowerCase();
555:
556: synchronized (aliases) {
557:
558: // Make sure this alias is currently present
559: int n = -1;
560: for (int i = 0; i < aliases.length; i++) {
561: if (aliases[i].equals(alias)) {
562: n = i;
563: break;
564: }
565: }
566: if (n < 0)
567: return;
568:
569: // Remove the specified alias
570: int j = 0;
571: String results[] = new String[aliases.length - 1];
572: for (int i = 0; i < aliases.length; i++) {
573: if (i != n)
574: results[j++] = aliases[i];
575: }
576: aliases = results;
577:
578: }
579:
580: // Inform interested listeners
581: fireContainerEvent(REMOVE_ALIAS_EVENT, alias);
582:
583: }
584:
585: /**
586: * Return a String representation of this component.
587: */
588: public String toString() {
589:
590: StringBuffer sb = new StringBuffer();
591: if (getParent() != null) {
592: sb.append(getParent().toString());
593: sb.append(".");
594: }
595: sb.append("StandardHost[");
596: sb.append(getName());
597: sb.append("]");
598: return (sb.toString());
599:
600: }
601:
602: /**
603: * Start this host.
604: *
605: * @exception LifecycleException if this component detects a fatal error
606: * that prevents it from being started
607: */
608: public synchronized void start() throws LifecycleException {
609: if (started) {
610: return;
611: }
612: if (!initialized)
613: init();
614:
615: // Look for a realm - that may have been configured earlier.
616: // If the realm is added after context - it'll set itself.
617: if (realm == null) {
618: ObjectName realmName = null;
619: try {
620: realmName = new ObjectName(domain + ":type=Realm,host="
621: + getName());
622: if (mserver.isRegistered(realmName)) {
623: mserver.invoke(realmName, "init", new Object[] {},
624: new String[] {});
625: }
626: } catch (Throwable t) {
627: log.debug("No realm for this host " + realmName);
628: }
629: }
630:
631: // Set error report valve
632: if ((errorReportValveClass != null)
633: && (!errorReportValveClass.equals(""))) {
634: try {
635: boolean found = false;
636: if (errorReportValveObjectName != null) {
637: ObjectName[] names = ((StandardPipeline) pipeline)
638: .getValveObjectNames();
639: for (int i = 0; !found && i < names.length; i++)
640: if (errorReportValveObjectName.equals(names[i]))
641: found = true;
642: }
643: if (!found) {
644: Valve valve = (Valve) Class.forName(
645: errorReportValveClass).newInstance();
646: addValve(valve);
647: errorReportValveObjectName = ((ValveBase) valve)
648: .getObjectName();
649: }
650: } catch (Throwable t) {
651: log.error(sm.getString(
652: "standardHost.invalidErrorReportValveClass",
653: errorReportValveClass));
654: }
655: }
656: if (log.isDebugEnabled()) {
657: if (xmlValidation)
658: log.debug(sm
659: .getString("standardHost.validationEnabled"));
660: else
661: log.debug(sm
662: .getString("standardHost.validationDisabled"));
663: }
664: super .start();
665:
666: }
667:
668: // -------------------- JMX --------------------
669: /**
670: * Return the MBean Names of the Valves assoicated with this Host
671: *
672: * @exception Exception if an MBean cannot be created or registered
673: */
674: public String[] getValveNames() throws Exception {
675: Valve[] valves = this .getValves();
676: String[] mbeanNames = new String[valves.length];
677: for (int i = 0; i < valves.length; i++) {
678: if (valves[i] == null)
679: continue;
680: if (((ValveBase) valves[i]).getObjectName() == null)
681: continue;
682: mbeanNames[i] = ((ValveBase) valves[i]).getObjectName()
683: .toString();
684: }
685:
686: return mbeanNames;
687:
688: }
689:
690: public String[] getAliases() {
691: return aliases;
692: }
693:
694: private boolean initialized = false;
695:
696: public void init() {
697: if (initialized)
698: return;
699: initialized = true;
700:
701: // already registered.
702: if (getParent() == null) {
703: try {
704: // Register with the Engine
705: ObjectName serviceName = new ObjectName(domain
706: + ":type=Engine");
707:
708: HostConfig deployer = new HostConfig();
709: addLifecycleListener(deployer);
710: if (mserver.isRegistered(serviceName)) {
711: if (log.isDebugEnabled())
712: log.debug("Registering " + serviceName
713: + " with the Engine");
714: mserver
715: .invoke(
716: serviceName,
717: "addChild",
718: new Object[] { this },
719: new String[] { "org.apache.catalina.Container" });
720: }
721: } catch (Exception ex) {
722: log.error("Host registering failed!", ex);
723: }
724: }
725:
726: if (oname == null) {
727: // not registered in JMX yet - standalone mode
728: try {
729: StandardEngine engine = (StandardEngine) parent;
730: domain = engine.getName();
731: if (log.isDebugEnabled())
732: log.debug("Register host " + getName()
733: + " with domain " + domain);
734: oname = new ObjectName(domain + ":type=Host,host="
735: + this .getName());
736: controller = oname;
737: Registry.getRegistry(null, null).registerComponent(
738: this , oname, null);
739: } catch (Throwable t) {
740: log.error("Host registering failed!", t);
741: }
742: }
743: }
744:
745: public void destroy() throws Exception {
746: // destroy our child containers, if any
747: Container children[] = findChildren();
748: super .destroy();
749: for (int i = 0; i < children.length; i++) {
750: if (children[i] instanceof StandardContext)
751: ((StandardContext) children[i]).destroy();
752: }
753:
754: }
755:
756: public ObjectName preRegister(MBeanServer server, ObjectName oname)
757: throws Exception {
758: ObjectName res = super .preRegister(server, oname);
759: String name = oname.getKeyProperty("host");
760: if (name != null)
761: setName(name);
762: return res;
763: }
764:
765: public ObjectName createObjectName(String domain, ObjectName parent)
766: throws Exception {
767: if (log.isDebugEnabled())
768: log.debug("Create ObjectName " + domain + " " + parent);
769: return new ObjectName(domain + ":type=Host,host=" + getName());
770: }
771:
772: }
|