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 java.io.File;
021: import java.io.InputStream;
022: import java.net.MalformedURLException;
023: import java.net.URL;
024: import java.util.ArrayList;
025: import java.util.Enumeration;
026: import java.util.Iterator;
027: import java.util.Map;
028: import java.util.Set;
029: import java.util.concurrent.ConcurrentHashMap;
030:
031: import javax.naming.Binding;
032: import javax.naming.NamingException;
033: import javax.naming.directory.DirContext;
034: import javax.servlet.RequestDispatcher;
035: import javax.servlet.Servlet;
036: import javax.servlet.ServletContext;
037: import javax.servlet.ServletContextAttributeEvent;
038: import javax.servlet.ServletContextAttributeListener;
039:
040: import org.apache.catalina.Context;
041: import org.apache.catalina.Host;
042: import org.apache.catalina.Wrapper;
043: import org.apache.catalina.deploy.ApplicationParameter;
044: import org.apache.catalina.util.Enumerator;
045: import org.apache.catalina.util.ResourceSet;
046: import org.apache.catalina.util.ServerInfo;
047: import org.apache.catalina.util.StringManager;
048: import org.apache.naming.resources.DirContextURLStreamHandler;
049: import org.apache.naming.resources.Resource;
050: import org.apache.tomcat.util.buf.CharChunk;
051: import org.apache.tomcat.util.buf.MessageBytes;
052: import org.apache.tomcat.util.http.mapper.MappingData;
053:
054: /**
055: * Standard implementation of <code>ServletContext</code> that represents
056: * a web application's execution environment. An instance of this class is
057: * associated with each instance of <code>StandardContext</code>.
058: *
059: * @author Craig R. McClanahan
060: * @author Remy Maucherat
061: * @version $Revision: 529466 $ $Date: 2007-04-17 03:52:50 +0200 (mar., 17 avr. 2007) $
062: */
063:
064: public class ApplicationContext implements ServletContext {
065:
066: // ----------------------------------------------------------- Constructors
067:
068: /**
069: * Construct a new instance of this class, associated with the specified
070: * Context instance.
071: *
072: * @param context The associated Context instance
073: */
074: public ApplicationContext(String basePath, StandardContext context) {
075: super ();
076: this .context = context;
077: this .basePath = basePath;
078: }
079:
080: // ----------------------------------------------------- Instance Variables
081:
082: /**
083: * The context attributes for this context.
084: */
085: protected Map attributes = new ConcurrentHashMap();
086:
087: /**
088: * List of read only attributes for this context.
089: */
090: private Map readOnlyAttributes = new ConcurrentHashMap();
091:
092: /**
093: * The Context instance with which we are associated.
094: */
095: private StandardContext context = null;
096:
097: /**
098: * Empty collection to serve as the basis for empty enumerations.
099: * <strong>DO NOT ADD ANY ELEMENTS TO THIS COLLECTION!</strong>
100: */
101: private static final ArrayList empty = new ArrayList();
102:
103: /**
104: * The facade around this object.
105: */
106: private ServletContext facade = new ApplicationContextFacade(this );
107:
108: /**
109: * The merged context initialization parameters for this Context.
110: */
111: private Map parameters = null;
112:
113: /**
114: * The string manager for this package.
115: */
116: private static final StringManager sm = StringManager
117: .getManager(Constants.Package);
118:
119: /**
120: * Base path.
121: */
122: private String basePath = null;
123:
124: /**
125: * Thread local data used during request dispatch.
126: */
127: private ThreadLocal<DispatchData> dispatchData = new ThreadLocal<DispatchData>();
128:
129: // --------------------------------------------------------- Public Methods
130:
131: /**
132: * Return the resources object that is mapped to a specified path.
133: * The path must begin with a "/" and is interpreted as relative to the
134: * current context root.
135: */
136: public DirContext getResources() {
137:
138: return context.getResources();
139:
140: }
141:
142: // ------------------------------------------------- ServletContext Methods
143:
144: /**
145: * Return the value of the specified context attribute, if any;
146: * otherwise return <code>null</code>.
147: *
148: * @param name Name of the context attribute to return
149: */
150: public Object getAttribute(String name) {
151:
152: return (attributes.get(name));
153:
154: }
155:
156: /**
157: * Return an enumeration of the names of the context attributes
158: * associated with this context.
159: */
160: public Enumeration getAttributeNames() {
161:
162: return new Enumerator(attributes.keySet(), true);
163:
164: }
165:
166: /**
167: * Return a <code>ServletContext</code> object that corresponds to a
168: * specified URI on the server. This method allows servlets to gain
169: * access to the context for various parts of the server, and as needed
170: * obtain <code>RequestDispatcher</code> objects or resources from the
171: * context. The given path must be absolute (beginning with a "/"),
172: * and is interpreted based on our virtual host's document root.
173: *
174: * @param uri Absolute URI of a resource on the server
175: */
176: public ServletContext getContext(String uri) {
177:
178: // Validate the format of the specified argument
179: if ((uri == null) || (!uri.startsWith("/")))
180: return (null);
181:
182: Context child = null;
183: try {
184: Host host = (Host) context.getParent();
185: String mapuri = uri;
186: while (true) {
187: child = (Context) host.findChild(mapuri);
188: if (child != null)
189: break;
190: int slash = mapuri.lastIndexOf('/');
191: if (slash < 0)
192: break;
193: mapuri = mapuri.substring(0, slash);
194: }
195: } catch (Throwable t) {
196: return (null);
197: }
198:
199: if (child == null)
200: return (null);
201:
202: if (context.getCrossContext()) {
203: // If crossContext is enabled, can always return the context
204: return child.getServletContext();
205: } else if (child == context) {
206: // Can still return the current context
207: return context.getServletContext();
208: } else {
209: // Nothing to return
210: return (null);
211: }
212: }
213:
214: /**
215: * Return the main path associated with this context.
216: */
217: public String getContextPath() {
218: return context.getPath();
219: }
220:
221: /**
222: * Return the value of the specified initialization parameter, or
223: * <code>null</code> if this parameter does not exist.
224: *
225: * @param name Name of the initialization parameter to retrieve
226: */
227: public String getInitParameter(final String name) {
228:
229: mergeParameters();
230: return ((String) parameters.get(name));
231:
232: }
233:
234: /**
235: * Return the names of the context's initialization parameters, or an
236: * empty enumeration if the context has no initialization parameters.
237: */
238: public Enumeration getInitParameterNames() {
239:
240: mergeParameters();
241: return (new Enumerator(parameters.keySet()));
242:
243: }
244:
245: /**
246: * Return the major version of the Java Servlet API that we implement.
247: */
248: public int getMajorVersion() {
249:
250: return (Constants.MAJOR_VERSION);
251:
252: }
253:
254: /**
255: * Return the minor version of the Java Servlet API that we implement.
256: */
257: public int getMinorVersion() {
258:
259: return (Constants.MINOR_VERSION);
260:
261: }
262:
263: /**
264: * Return the MIME type of the specified file, or <code>null</code> if
265: * the MIME type cannot be determined.
266: *
267: * @param file Filename for which to identify a MIME type
268: */
269: public String getMimeType(String file) {
270:
271: if (file == null)
272: return (null);
273: int period = file.lastIndexOf(".");
274: if (period < 0)
275: return (null);
276: String extension = file.substring(period + 1);
277: if (extension.length() < 1)
278: return (null);
279: return (context.findMimeMapping(extension));
280:
281: }
282:
283: /**
284: * Return a <code>RequestDispatcher</code> object that acts as a
285: * wrapper for the named servlet.
286: *
287: * @param name Name of the servlet for which a dispatcher is requested
288: */
289: public RequestDispatcher getNamedDispatcher(String name) {
290:
291: // Validate the name argument
292: if (name == null)
293: return (null);
294:
295: // Create and return a corresponding request dispatcher
296: Wrapper wrapper = (Wrapper) context.findChild(name);
297: if (wrapper == null)
298: return (null);
299:
300: return new ApplicationDispatcher(wrapper, null, null, null,
301: null, name);
302:
303: }
304:
305: /**
306: * Return the real path for a given virtual path, if possible; otherwise
307: * return <code>null</code>.
308: *
309: * @param path The path to the desired resource
310: */
311: public String getRealPath(String path) {
312:
313: if (!context.isFilesystemBased())
314: return null;
315:
316: if (path == null) {
317: return null;
318: }
319:
320: File file = new File(basePath, path);
321: return (file.getAbsolutePath());
322:
323: }
324:
325: /**
326: * Return a <code>RequestDispatcher</code> instance that acts as a
327: * wrapper for the resource at the given path. The path must begin
328: * with a "/" and is interpreted as relative to the current context root.
329: *
330: * @param path The path to the desired resource.
331: */
332: public RequestDispatcher getRequestDispatcher(String path) {
333:
334: // Validate the path argument
335: if (path == null)
336: return (null);
337: if (!path.startsWith("/"))
338: throw new IllegalArgumentException(sm.getString(
339: "applicationContext.requestDispatcher.iae", path));
340: path = normalize(path);
341: if (path == null)
342: return (null);
343:
344: // Use the thread local URI and mapping data
345: DispatchData dd = dispatchData.get();
346: if (dd == null) {
347: dd = new DispatchData();
348: dispatchData.set(dd);
349: }
350:
351: MessageBytes uriMB = dd.uriMB;
352: uriMB.recycle();
353:
354: // Get query string
355: String queryString = null;
356: int pos = path.indexOf('?');
357: if (pos >= 0) {
358: queryString = path.substring(pos + 1);
359: } else {
360: pos = path.length();
361: }
362:
363: // Use the thread local mapping data
364: MappingData mappingData = dd.mappingData;
365:
366: // Map the URI
367: CharChunk uriCC = uriMB.getCharChunk();
368: try {
369: uriCC.append(context.getPath(), 0, context.getPath()
370: .length());
371: /*
372: * Ignore any trailing path params (separated by ';') for mapping
373: * purposes
374: */
375: int semicolon = path.indexOf(';');
376: if (pos >= 0 && semicolon > pos) {
377: semicolon = -1;
378: }
379: uriCC.append(path, 0, semicolon > 0 ? semicolon : pos);
380: context.getMapper().map(uriMB, mappingData);
381: if (mappingData.wrapper == null) {
382: return (null);
383: }
384: /*
385: * Append any trailing path params (separated by ';') that were
386: * ignored for mapping purposes, so that they're reflected in the
387: * RequestDispatcher's requestURI
388: */
389: if (semicolon > 0) {
390: uriCC.append(path, semicolon, pos - semicolon);
391: }
392: } catch (Exception e) {
393: // Should never happen
394: log(sm.getString("applicationContext.mapping.error"), e);
395: return (null);
396: }
397:
398: Wrapper wrapper = (Wrapper) mappingData.wrapper;
399: String wrapperPath = mappingData.wrapperPath.toString();
400: String pathInfo = mappingData.pathInfo.toString();
401:
402: mappingData.recycle();
403:
404: // Construct a RequestDispatcher to process this request
405: return new ApplicationDispatcher(wrapper, uriCC.toString(),
406: wrapperPath, pathInfo, queryString, null);
407:
408: }
409:
410: /**
411: * Return the URL to the resource that is mapped to a specified path.
412: * The path must begin with a "/" and is interpreted as relative to the
413: * current context root.
414: *
415: * @param path The path to the desired resource
416: *
417: * @exception MalformedURLException if the path is not given
418: * in the correct form
419: */
420: public URL getResource(String path) throws MalformedURLException {
421:
422: if (path == null || !path.startsWith("/")) {
423: throw new MalformedURLException(sm.getString(
424: "applicationContext.requestDispatcher.iae", path));
425: }
426:
427: path = normalize(path);
428: if (path == null)
429: return (null);
430:
431: String libPath = "/WEB-INF/lib/";
432: if ((path.startsWith(libPath)) && (path.endsWith(".jar"))) {
433: File jarFile = null;
434: if (context.isFilesystemBased()) {
435: jarFile = new File(basePath, path);
436: } else {
437: jarFile = new File(context.getWorkPath(), path);
438: }
439: if (jarFile.exists()) {
440: return jarFile.toURL();
441: } else {
442: return null;
443: }
444: } else {
445:
446: DirContext resources = context.getResources();
447: if (resources != null) {
448: String fullPath = context.getName() + path;
449: String hostName = context.getParent().getName();
450: try {
451: resources.lookup(path);
452: return new URL("jndi", "", 0, getJNDIUri(hostName,
453: fullPath), new DirContextURLStreamHandler(
454: resources));
455: } catch (Exception e) {
456: // Ignore
457: }
458: }
459: }
460:
461: return (null);
462:
463: }
464:
465: /**
466: * Return the requested resource as an <code>InputStream</code>. The
467: * path must be specified according to the rules described under
468: * <code>getResource</code>. If no such resource can be identified,
469: * return <code>null</code>.
470: *
471: * @param path The path to the desired resource.
472: */
473: public InputStream getResourceAsStream(String path) {
474:
475: path = normalize(path);
476: if (path == null)
477: return (null);
478:
479: DirContext resources = context.getResources();
480: if (resources != null) {
481: try {
482: Object resource = resources.lookup(path);
483: if (resource instanceof Resource)
484: return (((Resource) resource).streamContent());
485: } catch (Exception e) {
486: }
487: }
488: return (null);
489:
490: }
491:
492: /**
493: * Return a Set containing the resource paths of resources member of the
494: * specified collection. Each path will be a String starting with
495: * a "/" character. The returned set is immutable.
496: *
497: * @param path Collection path
498: */
499: public Set getResourcePaths(String path) {
500:
501: // Validate the path argument
502: if (path == null) {
503: return null;
504: }
505: if (!path.startsWith("/")) {
506: throw new IllegalArgumentException(sm.getString(
507: "applicationContext.resourcePaths.iae", path));
508: }
509:
510: path = normalize(path);
511: if (path == null)
512: return (null);
513:
514: DirContext resources = context.getResources();
515: if (resources != null) {
516: return (getResourcePathsInternal(resources, path));
517: }
518: return (null);
519:
520: }
521:
522: /**
523: * Internal implementation of getResourcesPath() logic.
524: *
525: * @param resources Directory context to search
526: * @param path Collection path
527: */
528: private Set getResourcePathsInternal(DirContext resources,
529: String path) {
530:
531: ResourceSet set = new ResourceSet();
532: try {
533: listCollectionPaths(set, resources, path);
534: } catch (NamingException e) {
535: return (null);
536: }
537: set.setLocked(true);
538: return (set);
539:
540: }
541:
542: /**
543: * Return the name and version of the servlet container.
544: */
545: public String getServerInfo() {
546:
547: return (ServerInfo.getServerInfo());
548:
549: }
550:
551: /**
552: * @deprecated As of Java Servlet API 2.1, with no direct replacement.
553: */
554: public Servlet getServlet(String name) {
555:
556: return (null);
557:
558: }
559:
560: /**
561: * Return the display name of this web application.
562: */
563: public String getServletContextName() {
564:
565: return (context.getDisplayName());
566:
567: }
568:
569: /**
570: * @deprecated As of Java Servlet API 2.1, with no direct replacement.
571: */
572: public Enumeration getServletNames() {
573: return (new Enumerator(empty));
574: }
575:
576: /**
577: * @deprecated As of Java Servlet API 2.1, with no direct replacement.
578: */
579: public Enumeration getServlets() {
580: return (new Enumerator(empty));
581: }
582:
583: /**
584: * Writes the specified message to a servlet log file.
585: *
586: * @param message Message to be written
587: */
588: public void log(String message) {
589:
590: context.getLogger().info(message);
591:
592: }
593:
594: /**
595: * Writes the specified exception and message to a servlet log file.
596: *
597: * @param exception Exception to be reported
598: * @param message Message to be written
599: *
600: * @deprecated As of Java Servlet API 2.1, use
601: * <code>log(String, Throwable)</code> instead
602: */
603: public void log(Exception exception, String message) {
604:
605: context.getLogger().error(message, exception);
606:
607: }
608:
609: /**
610: * Writes the specified message and exception to a servlet log file.
611: *
612: * @param message Message to be written
613: * @param throwable Exception to be reported
614: */
615: public void log(String message, Throwable throwable) {
616:
617: context.getLogger().error(message, throwable);
618:
619: }
620:
621: /**
622: * Remove the context attribute with the specified name, if any.
623: *
624: * @param name Name of the context attribute to be removed
625: */
626: public void removeAttribute(String name) {
627:
628: Object value = null;
629: boolean found = false;
630:
631: // Remove the specified attribute
632: // Check for read only attribute
633: if (readOnlyAttributes.containsKey(name))
634: return;
635: found = attributes.containsKey(name);
636: if (found) {
637: value = attributes.get(name);
638: attributes.remove(name);
639: } else {
640: return;
641: }
642:
643: // Notify interested application event listeners
644: Object listeners[] = context.getApplicationEventListeners();
645: if ((listeners == null) || (listeners.length == 0))
646: return;
647: ServletContextAttributeEvent event = new ServletContextAttributeEvent(
648: context.getServletContext(), name, value);
649: for (int i = 0; i < listeners.length; i++) {
650: if (!(listeners[i] instanceof ServletContextAttributeListener))
651: continue;
652: ServletContextAttributeListener listener = (ServletContextAttributeListener) listeners[i];
653: try {
654: context.fireContainerEvent(
655: "beforeContextAttributeRemoved", listener);
656: listener.attributeRemoved(event);
657: context.fireContainerEvent(
658: "afterContextAttributeRemoved", listener);
659: } catch (Throwable t) {
660: context.fireContainerEvent(
661: "afterContextAttributeRemoved", listener);
662: // FIXME - should we do anything besides log these?
663: log(sm.getString("applicationContext.attributeEvent"),
664: t);
665: }
666: }
667:
668: }
669:
670: /**
671: * Bind the specified value with the specified context attribute name,
672: * replacing any existing value for that name.
673: *
674: * @param name Attribute name to be bound
675: * @param value New attribute value to be bound
676: */
677: public void setAttribute(String name, Object value) {
678:
679: // Name cannot be null
680: if (name == null)
681: throw new IllegalArgumentException(
682: sm
683: .getString("applicationContext.setAttribute.namenull"));
684:
685: // Null value is the same as removeAttribute()
686: if (value == null) {
687: removeAttribute(name);
688: return;
689: }
690:
691: Object oldValue = null;
692: boolean replaced = false;
693:
694: // Add or replace the specified attribute
695: // Check for read only attribute
696: if (readOnlyAttributes.containsKey(name))
697: return;
698: oldValue = attributes.get(name);
699: if (oldValue != null)
700: replaced = true;
701: attributes.put(name, value);
702:
703: // Notify interested application event listeners
704: Object listeners[] = context.getApplicationEventListeners();
705: if ((listeners == null) || (listeners.length == 0))
706: return;
707: ServletContextAttributeEvent event = null;
708: if (replaced)
709: event = new ServletContextAttributeEvent(context
710: .getServletContext(), name, oldValue);
711: else
712: event = new ServletContextAttributeEvent(context
713: .getServletContext(), name, value);
714:
715: for (int i = 0; i < listeners.length; i++) {
716: if (!(listeners[i] instanceof ServletContextAttributeListener))
717: continue;
718: ServletContextAttributeListener listener = (ServletContextAttributeListener) listeners[i];
719: try {
720: if (replaced) {
721: context.fireContainerEvent(
722: "beforeContextAttributeReplaced", listener);
723: listener.attributeReplaced(event);
724: context.fireContainerEvent(
725: "afterContextAttributeReplaced", listener);
726: } else {
727: context.fireContainerEvent(
728: "beforeContextAttributeAdded", listener);
729: listener.attributeAdded(event);
730: context.fireContainerEvent(
731: "afterContextAttributeAdded", listener);
732: }
733: } catch (Throwable t) {
734: if (replaced)
735: context.fireContainerEvent(
736: "afterContextAttributeReplaced", listener);
737: else
738: context.fireContainerEvent(
739: "afterContextAttributeAdded", listener);
740: // FIXME - should we do anything besides log these?
741: log(sm.getString("applicationContext.attributeEvent"),
742: t);
743: }
744: }
745:
746: }
747:
748: // -------------------------------------------------------- Package Methods
749: protected StandardContext getContext() {
750: return this .context;
751: }
752:
753: protected Map getReadonlyAttributes() {
754: return this .readOnlyAttributes;
755: }
756:
757: /**
758: * Clear all application-created attributes.
759: */
760: protected void clearAttributes() {
761:
762: // Create list of attributes to be removed
763: ArrayList list = new ArrayList();
764: Iterator iter = attributes.keySet().iterator();
765: while (iter.hasNext()) {
766: list.add(iter.next());
767: }
768:
769: // Remove application originated attributes
770: // (read only attributes will be left in place)
771: Iterator keys = list.iterator();
772: while (keys.hasNext()) {
773: String key = (String) keys.next();
774: removeAttribute(key);
775: }
776:
777: }
778:
779: /**
780: * Return the facade associated with this ApplicationContext.
781: */
782: protected ServletContext getFacade() {
783:
784: return (this .facade);
785:
786: }
787:
788: /**
789: * Set an attribute as read only.
790: */
791: void setAttributeReadOnly(String name) {
792:
793: if (attributes.containsKey(name))
794: readOnlyAttributes.put(name, name);
795:
796: }
797:
798: // -------------------------------------------------------- Private Methods
799:
800: /**
801: * Return a context-relative path, beginning with a "/", that represents
802: * the canonical version of the specified path after ".." and "." elements
803: * are resolved out. If the specified path attempts to go outside the
804: * boundaries of the current context (i.e. too many ".." path elements
805: * are present), return <code>null</code> instead.
806: *
807: * @param path Path to be normalized
808: */
809: private String normalize(String path) {
810:
811: if (path == null) {
812: return null;
813: }
814:
815: String normalized = path;
816:
817: // Normalize the slashes and add leading slash if necessary
818: if (normalized.indexOf('\\') >= 0)
819: normalized = normalized.replace('\\', '/');
820:
821: // Resolve occurrences of "/../" in the normalized path
822: while (true) {
823: int index = normalized.indexOf("/../");
824: if (index < 0)
825: break;
826: if (index == 0)
827: return (null); // Trying to go outside our context
828: int index2 = normalized.lastIndexOf('/', index - 1);
829: normalized = normalized.substring(0, index2)
830: + normalized.substring(index + 3);
831: }
832:
833: // Return the normalized path that we have completed
834: return (normalized);
835:
836: }
837:
838: /**
839: * Merge the context initialization parameters specified in the application
840: * deployment descriptor with the application parameters described in the
841: * server configuration, respecting the <code>override</code> property of
842: * the application parameters appropriately.
843: */
844: private void mergeParameters() {
845:
846: if (parameters != null)
847: return;
848: Map results = new ConcurrentHashMap();
849: String names[] = context.findParameters();
850: for (int i = 0; i < names.length; i++)
851: results.put(names[i], context.findParameter(names[i]));
852: ApplicationParameter params[] = context
853: .findApplicationParameters();
854: for (int i = 0; i < params.length; i++) {
855: if (params[i].getOverride()) {
856: if (results.get(params[i].getName()) == null)
857: results.put(params[i].getName(), params[i]
858: .getValue());
859: } else {
860: results.put(params[i].getName(), params[i].getValue());
861: }
862: }
863: parameters = results;
864:
865: }
866:
867: /**
868: * List resource paths (recursively), and store all of them in the given
869: * Set.
870: */
871: private static void listCollectionPaths(Set set,
872: DirContext resources, String path) throws NamingException {
873:
874: Enumeration childPaths = resources.listBindings(path);
875: while (childPaths.hasMoreElements()) {
876: Binding binding = (Binding) childPaths.nextElement();
877: String name = binding.getName();
878: StringBuffer childPath = new StringBuffer(path);
879: if (!"/".equals(path) && !path.endsWith("/"))
880: childPath.append("/");
881: childPath.append(name);
882: Object object = binding.getObject();
883: if (object instanceof DirContext) {
884: childPath.append("/");
885: }
886: set.add(childPath.toString());
887: }
888:
889: }
890:
891: /**
892: * Get full path, based on the host name and the context path.
893: */
894: private static String getJNDIUri(String hostName, String path) {
895: if (!path.startsWith("/"))
896: return "/" + hostName + "/" + path;
897: else
898: return "/" + hostName + path;
899: }
900:
901: /**
902: * Internal class used as thread-local storage when doing path
903: * mapping during dispatch.
904: */
905: private final class DispatchData {
906:
907: public MessageBytes uriMB;
908: public MappingData mappingData;
909:
910: public DispatchData() {
911: uriMB = MessageBytes.newInstance();
912: CharChunk uriCC = uriMB.getCharChunk();
913: uriCC.setLimit(-1);
914: mappingData = new MappingData();
915: }
916: }
917:
918: }
|