001: /*
002: * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/core/FastEngineMapper.java,v 1.4 2002/06/09 02:19:42 remm Exp $
003: * $Revision: 1.4 $
004: * $Date: 2002/06/09 02:19:42 $
005: *
006: * ====================================================================
007: *
008: * The Apache Software License, Version 1.1
009: *
010: * Copyright (c) 1999-2001 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.beans.PropertyChangeEvent;
067: import java.beans.PropertyChangeListener;
068: import java.util.ArrayList;
069: import java.util.Iterator;
070: import org.apache.catalina.Container;
071: import org.apache.catalina.ContainerEvent;
072: import org.apache.catalina.ContainerListener;
073: import org.apache.catalina.Engine;
074: import org.apache.catalina.Host;
075: import org.apache.catalina.Lifecycle;
076: import org.apache.catalina.LifecycleEvent;
077: import org.apache.catalina.LifecycleException;
078: import org.apache.catalina.LifecycleListener;
079: import org.apache.catalina.Mapper;
080: import org.apache.catalina.Request;
081: import org.apache.catalina.util.LifecycleSupport;
082: import org.apache.catalina.util.StringManager;
083:
084: /**
085: * Implementation of <code>Mapper</code> for an <code>Engine</code>,
086: * designed to process HTTP requests. This mapper selects an appropriate
087: * <code>Host</code> based on the server name included in the request.
088: * <p>
089: * <b>IMPLEMENTATION NOTE</b>: This Mapper only works with a
090: * <code>StandardEngine</code>, because it relies on internal APIs.
091: *
092: * @author Craig R. McClanahan
093: * @version $Revision: 1.4 $ $Date: 2002/06/09 02:19:42 $
094: */
095:
096: public final class FastEngineMapper implements ContainerListener,
097: Lifecycle, Mapper, PropertyChangeListener {
098:
099: // ----------------------------------------------------- Instance Variables
100:
101: /**
102: * Cache of hostname -> Host mappings. FIXME - use FastHashMap.
103: */
104: private java.util.HashMap cache = new java.util.HashMap();
105:
106: /**
107: * The default host used for unknown host names.
108: */
109: private Host defaultHost = null;
110:
111: /**
112: * The debugging detail level for this component.
113: */
114: private int debug = 0;
115:
116: /**
117: * The Container with which this Mapper is associated.
118: */
119: private StandardEngine engine = null;
120:
121: /**
122: * The lifecycle event support for this component.
123: */
124: private LifecycleSupport lifecycle = new LifecycleSupport(this );
125:
126: /**
127: * The protocol with which this Mapper is associated.
128: */
129: private String protocol = null;
130:
131: /**
132: * The string manager for this package.
133: */
134: private static final StringManager sm = StringManager
135: .getManager(Constants.Package);
136:
137: /**
138: * Has this component been started yet?
139: */
140: private boolean started = false;
141:
142: // ------------------------------------------------------------- Properties
143:
144: /**
145: * Return the Container with which this Mapper is associated.
146: */
147: public Container getContainer() {
148:
149: return (engine);
150:
151: }
152:
153: /**
154: * Set the Container with which this Mapper is associated.
155: *
156: * @param container The newly associated Container
157: *
158: * @exception IllegalArgumentException if this Container is not
159: * acceptable to this Mapper
160: */
161: public void setContainer(Container container) {
162:
163: if (!(container instanceof StandardEngine))
164: throw new IllegalArgumentException(sm
165: .getString("httpEngineMapper.container"));
166: engine = (StandardEngine) container;
167:
168: }
169:
170: /**
171: * Return the protocol for which this Mapper is responsible.
172: */
173: public String getProtocol() {
174:
175: return (this .protocol);
176:
177: }
178:
179: /**
180: * Set the protocol for which this Mapper is responsible.
181: *
182: * @param protocol The newly associated protocol
183: */
184: public void setProtocol(String protocol) {
185:
186: this .protocol = protocol;
187:
188: }
189:
190: // --------------------------------------------------------- Public Methods
191:
192: /**
193: * Return the child Container that should be used to process this Request,
194: * based upon its characteristics. If no such child Container can be
195: * identified, return <code>null</code> instead.
196: *
197: * @param request Request being processed
198: * @param update Update the Request to reflect the mapping selection?
199: */
200: public Container map(Request request, boolean update) {
201:
202: debug = engine.getDebug();
203:
204: // Extract the requested server name
205: String server = request.getRequest().getServerName();
206: if (server == null) {
207: server = engine.getDefaultHost();
208: if (update)
209: request.setServerName(server);
210: }
211: if (server == null)
212: return (null);
213: if (debug >= 1)
214: engine.log("Mapping server name '" + server + "'");
215:
216: // Find the specified host in our cache
217: if (debug >= 2)
218: engine.log(" Trying a cache match");
219: Host host = (Host) cache.get(server);
220:
221: // Map to the default host if any
222: if ((host == null) && (defaultHost != null)) {
223: if (debug >= 2)
224: engine.log(" Mapping to default host");
225: host = defaultHost;
226: addAlias(server, host);
227: }
228:
229: // Update the Request if requested, and return the selected Host
230: ; // No update to the Request is required
231: return (host);
232:
233: }
234:
235: // ---------------------------------------------- ContainerListener Methods
236:
237: /**
238: * Acknowledge the occurrence of the specified event.
239: *
240: * @param event ContainerEvent that has occurred
241: */
242: public void containerEvent(ContainerEvent event) {
243:
244: Container source = (Container) event.getSource();
245: String type = event.getType();
246: if (source == engine) {
247: if (Container.ADD_CHILD_EVENT.equals(type))
248: addHost((Host) event.getData());
249: else if (Container.REMOVE_CHILD_EVENT.equals(type))
250: removeHost((Host) event.getData());
251: } else if (source instanceof Host) {
252: if (Host.ADD_ALIAS_EVENT.equals(type))
253: addAlias((String) event.getData(), (Host) source);
254: else if (Host.REMOVE_ALIAS_EVENT.equals(type))
255: removeAlias((String) event.getData());
256: }
257:
258: }
259:
260: // ------------------------------------------------------ Lifecycle Methods
261:
262: /**
263: * Add a lifecycle event listener to this component.
264: *
265: * @param listener The listener to add
266: */
267: public void addLifecycleListener(LifecycleListener listener) {
268:
269: lifecycle.addLifecycleListener(listener);
270:
271: }
272:
273: /**
274: * Get the lifecycle listeners associated with this lifecycle. If this
275: * Lifecycle has no listeners registered, a zero-length array is returned.
276: */
277: public LifecycleListener[] findLifecycleListeners() {
278:
279: return lifecycle.findLifecycleListeners();
280:
281: }
282:
283: /**
284: * Remove a lifecycle event listener from this component.
285: *
286: * @param listener The listener to remove
287: */
288: public void removeLifecycleListener(LifecycleListener listener) {
289:
290: lifecycle.removeLifecycleListener(listener);
291:
292: }
293:
294: /**
295: * Prepare for active use of the public methods of this Component.
296: *
297: * @exception LifecycleException if this component detects a fatal error
298: * that prevents it from being started
299: */
300: public synchronized void start() throws LifecycleException {
301:
302: // Validate and update our current component state
303: if (started)
304: throw new LifecycleException(sm
305: .getString("fastEngineMapper.alreadyStarted",
306: engine.getName()));
307: started = true;
308:
309: // Configure based on our associated Engine properties
310: engine.addContainerListener(this );
311: engine.addPropertyChangeListener(this );
312: setDefaultHost(engine.getDefaultHost());
313:
314: // Cache mappings for our child hosts
315: Container children[] = engine.findChildren();
316: for (int i = 0; i < children.length; i++) {
317: addHost((Host) children[i]);
318: }
319:
320: // Notify our interested LifecycleListeners
321: lifecycle.fireLifecycleEvent(START_EVENT, null);
322:
323: }
324:
325: /**
326: * Gracefully shut down active use of the public methods of this Component.
327: *
328: * @exception LifecycleException if this component detects a fatal error
329: * that needs to be reported
330: */
331: public synchronized void stop() throws LifecycleException {
332:
333: // Validate and update our current component state
334: if (!started)
335: throw new LifecycleException(sm.getString(
336: "fastEngineMapper.notStarted", engine.getName()));
337:
338: // Notify our interested LifecycleListeners
339: lifecycle.fireLifecycleEvent(STOP_EVENT, null);
340: started = false;
341:
342: // Deconfigure based on our associated Engine properties
343: engine.removePropertyChangeListener(this );
344: setDefaultHost(null);
345: engine.removeContainerListener(this );
346:
347: // Clear our mapping cache
348: cache.clear();
349:
350: }
351:
352: // ----------------------------------------- PropertyChangeListener Methods
353:
354: /**
355: * Process a property change event.
356: */
357: public void propertyChange(PropertyChangeEvent event) {
358:
359: Object source = event.getSource();
360: if (source instanceof Engine) {
361: if ("defaultHost".equals(event.getPropertyName()))
362: setDefaultHost((String) event.getNewValue());
363: }
364:
365: }
366:
367: // -------------------------------------------------------- Private Methods
368:
369: /**
370: * Add an alias for the specified host.
371: *
372: * @param alias New alias name
373: * @param host Host to resolve to
374: */
375: private void addAlias(String alias, Host host) {
376:
377: if (debug >= 3)
378: engine.log("Adding alias '" + alias + "' for host '"
379: + host.getName() + "'");
380: cache.put(alias.toLowerCase(), host);
381:
382: }
383:
384: /**
385: * Add a new child Host to our associated Engine.
386: *
387: * @param host Child host to add
388: */
389: private void addHost(Host host) {
390:
391: if (debug >= 3)
392: engine.log("Adding host '" + host.getName() + "'");
393:
394: host.addContainerListener(this );
395:
396: // Register the host name
397: addAlias(host.getName(), host);
398:
399: // Register all associated aliases
400: String aliases[] = host.findAliases();
401: for (int i = 0; i < aliases.length; i++)
402: addAlias(aliases[i], host);
403:
404: }
405:
406: /**
407: * Return the Host that matches the specified name (or alias), if any;
408: * otherwise, return <code>null</code>.
409: *
410: * @param name Name or alias of the desired Host
411: */
412: private Host findHost(String name) {
413:
414: return ((Host) cache.get(name.toLowerCase()));
415:
416: }
417:
418: /**
419: * Remove the specified alias from our cache.
420: *
421: * @param alias Alias to remove
422: */
423: private void removeAlias(String alias) {
424:
425: if (debug >= 3)
426: engine.log("Removing alias '" + alias + "'");
427: cache.remove(alias.toLowerCase());
428:
429: }
430:
431: /**
432: * Remove an existing child Host from our associated Engine.
433: *
434: * @param host Host to be removed
435: */
436: private void removeHost(Host host) {
437:
438: if (debug >= 3)
439: engine.log("Removing host '" + host.getName() + "'");
440:
441: host.removeContainerListener(this );
442:
443: // Identify all names mapped to this host
444: ArrayList removes = new ArrayList();
445: Iterator keys = cache.keySet().iterator();
446: while (keys.hasNext()) {
447: String key = (String) keys.next();
448: if (host.equals((Host) cache.get(key)))
449: removes.add(key);
450: }
451:
452: // Remove the associated names
453: keys = removes.iterator();
454: while (keys.hasNext()) {
455: removeAlias((String) keys.next());
456: }
457:
458: }
459:
460: /**
461: * Set the default Host used for resolving unknown host names.
462: *
463: * @param name Name of the default host
464: */
465: private void setDefaultHost(String name) {
466:
467: if (debug >= 3)
468: engine.log("Setting default host '" + name + "'");
469:
470: if (name == null)
471: defaultHost = null;
472: else
473: defaultHost = (Host) engine.findChild(name);
474:
475: }
476:
477: }
|