0001: /*
0002: * Copyright 1999-2004 The Apache Software Foundation
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016:
0017: package org.apache.tomcat.util.http.mapper;
0018:
0019: import javax.naming.NamingException;
0020: import javax.naming.directory.DirContext;
0021:
0022: import org.apache.tomcat.util.buf.CharChunk;
0023: import org.apache.tomcat.util.buf.MessageBytes;
0024: import java.util.List;
0025: import java.util.ArrayList;
0026:
0027: /**
0028: * Mapper, which implements the servlet API mapping rules (which are derived
0029: * from the HTTP rules).
0030: *
0031: * @author Remy Maucherat
0032: */
0033: public final class Mapper {
0034:
0035: private static org.apache.commons.logging.Log logger = org.apache.commons.logging.LogFactory
0036: .getLog(Mapper.class);
0037: // ----------------------------------------------------- Instance Variables
0038:
0039: /**
0040: * Array containing the virtual hosts definitions.
0041: */
0042: protected Host[] hosts = new Host[0];
0043:
0044: /**
0045: * Default host name.
0046: */
0047: protected String defaultHostName = null;
0048:
0049: /**
0050: * Context associated with this wrapper, used for wrapper mapping.
0051: */
0052: protected Context context = new Context();
0053:
0054: // --------------------------------------------------------- Public Methods
0055:
0056: /**
0057: * Get default host.
0058: *
0059: * @return Default host name
0060: */
0061: public String getDefaultHostName() {
0062: return defaultHostName;
0063: }
0064:
0065: /**
0066: * Set default host.
0067: *
0068: * @param name Default host name
0069: */
0070: public void setDefaultHostName(String defaultHostName) {
0071: this .defaultHostName = defaultHostName;
0072: }
0073:
0074: /**
0075: * Add a new host to the mapper.
0076: *
0077: * @param name Virtual host name
0078: * @param host Host object
0079: */
0080: public synchronized void addHost(String name, String[] aliases,
0081: Object host) {
0082: Host[] newHosts = new Host[hosts.length + 1];
0083: Host newHost = new Host();
0084: ContextList contextList = new ContextList();
0085: newHost.name = name;
0086: newHost.contextList = contextList;
0087: newHost.object = host;
0088: if (insertMap(hosts, newHosts, newHost)) {
0089: hosts = newHosts;
0090: }
0091: for (int i = 0; i < aliases.length; i++) {
0092: newHosts = new Host[hosts.length + 1];
0093: newHost = new Host();
0094: newHost.name = aliases[i];
0095: newHost.contextList = contextList;
0096: newHost.object = host;
0097: if (insertMap(hosts, newHosts, newHost)) {
0098: hosts = newHosts;
0099: }
0100: }
0101: }
0102:
0103: /**
0104: * Remove a host from the mapper.
0105: *
0106: * @param name Virtual host name
0107: */
0108: public synchronized void removeHost(String name) {
0109: // Find and remove the old host
0110: int pos = find(hosts, name);
0111: if (pos < 0) {
0112: return;
0113: }
0114: Object host = hosts[pos].object;
0115: Host[] newHosts = new Host[hosts.length - 1];
0116: if (removeMap(hosts, newHosts, name)) {
0117: hosts = newHosts;
0118: }
0119: // Remove all aliases (they will map to the same host object)
0120: for (int i = 0; i < newHosts.length; i++) {
0121: if (newHosts[i].object == host) {
0122: Host[] newHosts2 = new Host[hosts.length - 1];
0123: if (removeMap(hosts, newHosts2, newHosts[i].name)) {
0124: hosts = newHosts2;
0125: }
0126: }
0127: }
0128: }
0129:
0130: public String[] getHosts() {
0131: String hostN[] = new String[hosts.length];
0132: for (int i = 0; i < hosts.length; i++) {
0133: hostN[i] = hosts[i].name;
0134: }
0135: return hostN;
0136: }
0137:
0138: /**
0139: * Set context, used for wrapper mapping (request dispatcher).
0140: *
0141: * @param welcomeResources Welcome files defined for this context
0142: * @param resources Static resources of the context
0143: */
0144: public void setContext(String path, String[] welcomeResources,
0145: javax.naming.Context resources) {
0146: context.name = path;
0147: context.welcomeResources = welcomeResources;
0148: context.resources = resources;
0149: }
0150:
0151: /**
0152: * Add a new Context to an existing Host.
0153: *
0154: * @param hostName Virtual host name this context belongs to
0155: * @param path Context path
0156: * @param context Context object
0157: * @param welcomeResources Welcome files defined for this context
0158: * @param resources Static resources of the context
0159: */
0160: public void addContext(String hostName, String path,
0161: Object context, String[] welcomeResources,
0162: javax.naming.Context resources) {
0163:
0164: Host[] hosts = this .hosts;
0165: int pos = find(hosts, hostName);
0166: if (pos < 0) {
0167: addHost(hostName, new String[0], "");
0168: hosts = this .hosts;
0169: pos = find(hosts, hostName);
0170: }
0171: if (pos < 0) {
0172: logger.error("No host found: " + hostName);
0173: }
0174: Host host = hosts[pos];
0175: if (host.name.equals(hostName)) {
0176: Context[] contexts = host.contextList.contexts;
0177: // Update nesting
0178: int slashCount = slashCount(path);
0179: if (slashCount > host.contextList.nesting) {
0180: host.contextList.nesting = slashCount;
0181: }
0182: synchronized (host) {
0183: Context[] newContexts = new Context[contexts.length + 1];
0184: Context newContext = new Context();
0185: newContext.name = path;
0186: newContext.object = context;
0187: newContext.welcomeResources = welcomeResources;
0188: newContext.resources = resources;
0189: if (insertMap(contexts, newContexts, newContext)) {
0190: host.contextList.contexts = newContexts;
0191: }
0192: }
0193: }
0194:
0195: }
0196:
0197: /**
0198: * Remove a context from an existing host.
0199: *
0200: * @param hostName Virtual host name this context belongs to
0201: * @param path Context path
0202: */
0203: public void removeContext(String hostName, String path) {
0204: Host[] hosts = this .hosts;
0205: int pos = find(hosts, hostName);
0206: if (pos < 0) {
0207: return;
0208: }
0209: Host host = hosts[pos];
0210: if (host.name.equals(hostName)) {
0211: Context[] contexts = host.contextList.contexts;
0212: if (contexts.length == 0) {
0213: return;
0214: }
0215: synchronized (host) {
0216: Context[] newContexts = new Context[contexts.length - 1];
0217: if (removeMap(contexts, newContexts, path)) {
0218: host.contextList.contexts = newContexts;
0219: // Recalculate nesting
0220: host.contextList.nesting = 0;
0221: for (int i = 0; i < newContexts.length; i++) {
0222: int slashCount = slashCount(newContexts[i].name);
0223: if (slashCount > host.contextList.nesting) {
0224: host.contextList.nesting = slashCount;
0225: }
0226: }
0227: }
0228: }
0229: }
0230: }
0231:
0232: /**
0233: * Return all contexts, in //HOST/PATH form
0234: *
0235: * @return
0236: */
0237: public String[] getContextNames() {
0238: List list = new ArrayList();
0239: for (int i = 0; i < hosts.length; i++) {
0240: for (int j = 0; j < hosts[i].contextList.contexts.length; j++) {
0241: String cname = hosts[i].contextList.contexts[j].name;
0242: list.add("//" + hosts[i].name
0243: + (cname.startsWith("/") ? cname : "/"));
0244: }
0245: }
0246: String res[] = new String[list.size()];
0247: return (String[]) list.toArray(res);
0248: }
0249:
0250: /**
0251: * Add a new Wrapper to an existing Context.
0252: *
0253: * @param hostName Virtual host name this wrapper belongs to
0254: * @param contextPath Context path this wrapper belongs to
0255: * @param path Wrapper mapping
0256: * @param wrapper Wrapper object
0257: */
0258: public void addWrapper(String hostName, String contextPath,
0259: String path, Object wrapper) {
0260: addWrapper(hostName, contextPath, path, wrapper, false);
0261: }
0262:
0263: public void addWrapper(String hostName, String contextPath,
0264: String path, Object wrapper, boolean jspWildCard) {
0265: Host[] hosts = this .hosts;
0266: int pos = find(hosts, hostName);
0267: if (pos < 0) {
0268: return;
0269: }
0270: Host host = hosts[pos];
0271: if (host.name.equals(hostName)) {
0272: Context[] contexts = host.contextList.contexts;
0273: int pos2 = find(contexts, contextPath);
0274: if (pos2 < 0) {
0275: logger.error("No context found: " + contextPath);
0276: return;
0277: }
0278: Context context = contexts[pos2];
0279: if (context.name.equals(contextPath)) {
0280: addWrapper(context, path, wrapper, jspWildCard);
0281: }
0282: }
0283: }
0284:
0285: /**
0286: * Add a wrapper to the context associated with this wrapper.
0287: *
0288: * @param path Wrapper mapping
0289: * @param wrapper The Wrapper object
0290: */
0291: public void addWrapper(String path, Object wrapper) {
0292: addWrapper(context, path, wrapper);
0293: }
0294:
0295: public void addWrapper(String path, Object wrapper,
0296: boolean jspWildCard) {
0297: addWrapper(context, path, wrapper, jspWildCard);
0298: }
0299:
0300: protected void addWrapper(Context context, String path,
0301: Object wrapper) {
0302: addWrapper(context, path, wrapper, false);
0303: }
0304:
0305: /**
0306: * Adds a wrapper to the given context.
0307: *
0308: * @param context The context to which to add the wrapper
0309: * @param path Wrapper mapping
0310: * @param wrapper The Wrapper object
0311: * @param jspWildCard true if the wrapper corresponds to the JspServlet
0312: * and the mapping path contains a wildcard; false otherwise
0313: */
0314: protected void addWrapper(Context context, String path,
0315: Object wrapper, boolean jspWildCard) {
0316:
0317: synchronized (context) {
0318: Wrapper newWrapper = new Wrapper();
0319: newWrapper.object = wrapper;
0320: newWrapper.jspWildCard = jspWildCard;
0321: if (path.endsWith("/*")) {
0322: // Wildcard wrapper
0323: newWrapper.name = path.substring(0, path.length() - 2);
0324: Wrapper[] oldWrappers = context.wildcardWrappers;
0325: Wrapper[] newWrappers = new Wrapper[oldWrappers.length + 1];
0326: if (insertMap(oldWrappers, newWrappers, newWrapper)) {
0327: context.wildcardWrappers = newWrappers;
0328: int slashCount = slashCount(newWrapper.name);
0329: if (slashCount > context.nesting) {
0330: context.nesting = slashCount;
0331: }
0332: }
0333: } else if (path.startsWith("*.")) {
0334: // Extension wrapper
0335: newWrapper.name = path.substring(2);
0336: Wrapper[] oldWrappers = context.extensionWrappers;
0337: Wrapper[] newWrappers = new Wrapper[oldWrappers.length + 1];
0338: if (insertMap(oldWrappers, newWrappers, newWrapper)) {
0339: context.extensionWrappers = newWrappers;
0340: }
0341: } else if (path.equals("/")) {
0342: // Default wrapper
0343: newWrapper.name = "";
0344: context.defaultWrapper = newWrapper;
0345: } else {
0346: // Exact wrapper
0347: newWrapper.name = path;
0348: Wrapper[] oldWrappers = context.exactWrappers;
0349: Wrapper[] newWrappers = new Wrapper[oldWrappers.length + 1];
0350: if (insertMap(oldWrappers, newWrappers, newWrapper)) {
0351: context.exactWrappers = newWrappers;
0352: }
0353: }
0354: }
0355: }
0356:
0357: /**
0358: * Remove a wrapper from the context associated with this wrapper.
0359: *
0360: * @param path Wrapper mapping
0361: */
0362: public void removeWrapper(String path) {
0363: removeWrapper(context, path);
0364: }
0365:
0366: /**
0367: * Remove a wrapper from an existing context.
0368: *
0369: * @param hostName Virtual host name this wrapper belongs to
0370: * @param contextPath Context path this wrapper belongs to
0371: * @param path Wrapper mapping
0372: */
0373: public void removeWrapper(String hostName, String contextPath,
0374: String path) {
0375: Host[] hosts = this .hosts;
0376: int pos = find(hosts, hostName);
0377: if (pos < 0) {
0378: return;
0379: }
0380: Host host = hosts[pos];
0381: if (host.name.equals(hostName)) {
0382: Context[] contexts = host.contextList.contexts;
0383: int pos2 = find(contexts, contextPath);
0384: if (pos2 < 0) {
0385: return;
0386: }
0387: Context context = contexts[pos2];
0388: if (context.name.equals(contextPath)) {
0389: removeWrapper(context, path);
0390: }
0391: }
0392: }
0393:
0394: protected void removeWrapper(Context context, String path) {
0395: synchronized (context) {
0396: if (path.endsWith("/*")) {
0397: // Wildcard wrapper
0398: String name = path.substring(0, path.length() - 2);
0399: Wrapper[] oldWrappers = context.wildcardWrappers;
0400: Wrapper[] newWrappers = new Wrapper[oldWrappers.length - 1];
0401: if (removeMap(oldWrappers, newWrappers, name)) {
0402: // Recalculate nesting
0403: context.nesting = 0;
0404: for (int i = 0; i < newWrappers.length; i++) {
0405: int slashCount = slashCount(newWrappers[i].name);
0406: if (slashCount > context.nesting) {
0407: context.nesting = slashCount;
0408: }
0409: }
0410: context.wildcardWrappers = newWrappers;
0411: }
0412: } else if (path.startsWith("*.")) {
0413: // Extension wrapper
0414: String name = path.substring(2);
0415: Wrapper[] oldWrappers = context.extensionWrappers;
0416: Wrapper[] newWrappers = new Wrapper[oldWrappers.length - 1];
0417: if (removeMap(oldWrappers, newWrappers, name)) {
0418: context.extensionWrappers = newWrappers;
0419: }
0420: } else if (path.equals("/")) {
0421: // Default wrapper
0422: context.defaultWrapper = null;
0423: } else {
0424: // Exact wrapper
0425: String name = path;
0426: Wrapper[] oldWrappers = context.exactWrappers;
0427: Wrapper[] newWrappers = new Wrapper[oldWrappers.length - 1];
0428: if (removeMap(oldWrappers, newWrappers, name)) {
0429: context.exactWrappers = newWrappers;
0430: }
0431: }
0432: }
0433: }
0434:
0435: public String getWrappersString(String host, String context) {
0436: String names[] = getWrapperNames(host, context);
0437: StringBuffer sb = new StringBuffer();
0438: for (int i = 0; i < names.length; i++) {
0439: sb.append(names[i]).append(":");
0440: }
0441: return sb.toString();
0442: }
0443:
0444: public String[] getWrapperNames(String host, String context) {
0445: List list = new ArrayList();
0446: if (host == null)
0447: host = "";
0448: if (context == null)
0449: context = "";
0450: for (int i = 0; i < hosts.length; i++) {
0451: if (!host.equals(hosts[i].name))
0452: continue;
0453: for (int j = 0; j < hosts[i].contextList.contexts.length; j++) {
0454: if (!context
0455: .equals(hosts[i].contextList.contexts[j].name))
0456: continue;
0457: // found the context
0458: Context ctx = hosts[i].contextList.contexts[j];
0459: list.add(ctx.defaultWrapper.path);
0460: for (int k = 0; k < ctx.exactWrappers.length; k++) {
0461: list.add(ctx.exactWrappers[k].path);
0462: }
0463: for (int k = 0; k < ctx.wildcardWrappers.length; k++) {
0464: list.add(ctx.wildcardWrappers[k].path + "*");
0465: }
0466: for (int k = 0; k < ctx.extensionWrappers.length; k++) {
0467: list.add("*." + ctx.extensionWrappers[k].path);
0468: }
0469: }
0470: }
0471: String res[] = new String[list.size()];
0472: return (String[]) list.toArray(res);
0473: }
0474:
0475: /**
0476: * Map the specified host name and URI, mutating the given mapping data.
0477: *
0478: * @param host Virtual host name
0479: * @param uri URI
0480: * @param mappingData This structure will contain the result of the mapping
0481: * operation
0482: */
0483: public void map(MessageBytes host, MessageBytes uri,
0484: MappingData mappingData) throws Exception {
0485:
0486: host.toChars();
0487: uri.toChars();
0488: internalMap(host.getCharChunk(), uri.getCharChunk(),
0489: mappingData);
0490:
0491: }
0492:
0493: /**
0494: * Map the specified URI relative to the context,
0495: * mutating the given mapping data.
0496: *
0497: * @param uri URI
0498: * @param mappingData This structure will contain the result of the mapping
0499: * operation
0500: */
0501: public void map(MessageBytes uri, MappingData mappingData)
0502: throws Exception {
0503:
0504: uri.toChars();
0505: CharChunk uricc = uri.getCharChunk();
0506: uricc.setLimit(-1);
0507: internalMapWrapper(context, uricc, mappingData);
0508:
0509: }
0510:
0511: // -------------------------------------------------------- Private Methods
0512:
0513: /**
0514: * Map the specified URI.
0515: */
0516: private final void internalMap(CharChunk host, CharChunk uri,
0517: MappingData mappingData) throws Exception {
0518:
0519: uri.setLimit(-1);
0520:
0521: Context[] contexts = null;
0522: Context context = null;
0523: int nesting = 0;
0524:
0525: // Virtual host mapping
0526: if (mappingData.host == null) {
0527: Host[] hosts = this .hosts;
0528: int pos = find(hosts, host);
0529: if ((pos != -1) && (host.equals(hosts[pos].name))) {
0530: mappingData.host = hosts[pos].object;
0531: contexts = hosts[pos].contextList.contexts;
0532: nesting = hosts[pos].contextList.nesting;
0533: } else {
0534: if (defaultHostName == null) {
0535: return;
0536: }
0537: pos = find(hosts, defaultHostName);
0538: if ((pos != -1)
0539: && (defaultHostName.equals(hosts[pos].name))) {
0540: mappingData.host = hosts[pos].object;
0541: contexts = hosts[pos].contextList.contexts;
0542: nesting = hosts[pos].contextList.nesting;
0543: } else {
0544: return;
0545: }
0546: }
0547: }
0548:
0549: // Context mapping
0550: if (mappingData.context == null) {
0551: int pos = find(contexts, uri);
0552: if (pos == -1) {
0553: return;
0554: }
0555:
0556: int lastSlash = -1;
0557: int uriEnd = uri.getEnd();
0558: int length = -1;
0559: boolean found = false;
0560: while (pos >= 0) {
0561: if (uri.startsWith(contexts[pos].name)) {
0562: length = contexts[pos].name.length();
0563: if (uri.getLength() == length) {
0564: found = true;
0565: break;
0566: } else if (uri.startsWithIgnoreCase("/", length)) {
0567: found = true;
0568: break;
0569: }
0570: }
0571: if (lastSlash == -1) {
0572: lastSlash = nthSlash(uri, nesting + 1);
0573: } else {
0574: lastSlash = lastSlash(uri);
0575: }
0576: uri.setEnd(lastSlash);
0577: pos = find(contexts, uri);
0578: }
0579: uri.setEnd(uriEnd);
0580:
0581: if (!found) {
0582: if (contexts[0].name.equals("")) {
0583: context = contexts[0];
0584: }
0585: } else {
0586: context = contexts[pos];
0587: }
0588: if (context != null) {
0589: mappingData.context = context.object;
0590: mappingData.contextPath.setString(context.name);
0591: }
0592: }
0593:
0594: // Wrapper mapping
0595: if ((context != null) && (mappingData.wrapper == null)) {
0596: internalMapWrapper(context, uri, mappingData);
0597: }
0598:
0599: }
0600:
0601: /**
0602: * Wrapper mapping.
0603: */
0604: private final void internalMapWrapper(Context context,
0605: CharChunk path, MappingData mappingData) throws Exception {
0606:
0607: int pathOffset = path.getOffset();
0608: int pathEnd = path.getEnd();
0609: int servletPath = pathOffset;
0610: boolean noServletPath = false;
0611:
0612: int length = context.name.length();
0613: if (length != (pathEnd - pathOffset)) {
0614: servletPath = pathOffset + length;
0615: } else {
0616: noServletPath = true;
0617: path.append('/');
0618: pathOffset = path.getOffset();
0619: pathEnd = path.getEnd();
0620: servletPath = pathOffset + length;
0621: }
0622:
0623: path.setOffset(servletPath);
0624:
0625: // Rule 1 -- Exact Match
0626: Wrapper[] exactWrappers = context.exactWrappers;
0627: internalMapExactWrapper(exactWrappers, path, mappingData);
0628:
0629: // Rule 2 -- Prefix Match
0630: boolean checkJspWelcomeFiles = false;
0631: Wrapper[] wildcardWrappers = context.wildcardWrappers;
0632: if (mappingData.wrapper == null) {
0633: internalMapWildcardWrapper(wildcardWrappers,
0634: context.nesting, path, mappingData);
0635: if (mappingData.wrapper != null && mappingData.jspWildCard) {
0636: char[] buf = path.getBuffer();
0637: if (buf[pathEnd - 1] == '/') {
0638: /*
0639: * Path ending in '/' was mapped to JSP servlet based on
0640: * wildcard match (e.g., as specified in url-pattern of a
0641: * jsp-property-group.
0642: * Force the context's welcome files, which are interpreted
0643: * as JSP files (since they match the url-pattern), to be
0644: * considered. See Bugzilla 27664.
0645: */
0646: mappingData.wrapper = null;
0647: checkJspWelcomeFiles = true;
0648: } else {
0649: // See Bugzilla 27704
0650: mappingData.wrapperPath.setChars(buf, path
0651: .getStart(), path.getLength());
0652: mappingData.pathInfo.recycle();
0653: }
0654: }
0655: }
0656:
0657: if (mappingData.wrapper == null && noServletPath) {
0658: // The path is empty, redirect to "/"
0659: mappingData.redirectPath.setChars(path.getBuffer(),
0660: pathOffset, pathEnd);
0661: path.setEnd(pathEnd - 1);
0662: return;
0663: }
0664:
0665: // Rule 3 -- Extension Match
0666: Wrapper[] extensionWrappers = context.extensionWrappers;
0667: if (mappingData.wrapper == null && !checkJspWelcomeFiles) {
0668: internalMapExtensionWrapper(extensionWrappers, path,
0669: mappingData);
0670: }
0671:
0672: // Rule 4 -- Welcome resources processing for servlets
0673: if (mappingData.wrapper == null) {
0674: boolean checkWelcomeFiles = checkJspWelcomeFiles;
0675: if (!checkWelcomeFiles) {
0676: char[] buf = path.getBuffer();
0677: checkWelcomeFiles = (buf[pathEnd - 1] == '/');
0678: }
0679: if (checkWelcomeFiles) {
0680: for (int i = 0; (i < context.welcomeResources.length)
0681: && (mappingData.wrapper == null); i++) {
0682: path.setOffset(pathOffset);
0683: path.setEnd(pathEnd);
0684: path.append(context.welcomeResources[i], 0,
0685: context.welcomeResources[i].length());
0686: path.setOffset(servletPath);
0687:
0688: // Rule 4a -- Welcome resources processing for exact macth
0689: internalMapExactWrapper(exactWrappers, path,
0690: mappingData);
0691:
0692: // Rule 4b -- Welcome resources processing for prefix match
0693: if (mappingData.wrapper == null) {
0694: internalMapWildcardWrapper(wildcardWrappers,
0695: context.nesting, path, mappingData);
0696: }
0697:
0698: // Rule 4c -- Welcome resources processing
0699: // for physical folder
0700: if (mappingData.wrapper == null
0701: && context.resources != null) {
0702: Object file = null;
0703: String pathStr = path.toString();
0704: try {
0705: file = context.resources.lookup(pathStr);
0706: } catch (NamingException nex) {
0707: // Swallow not found, since this is normal
0708: }
0709: if (file != null
0710: && !(file instanceof DirContext)) {
0711: internalMapExtensionWrapper(
0712: extensionWrappers, path,
0713: mappingData);
0714: if (mappingData.wrapper == null
0715: && context.defaultWrapper != null) {
0716: mappingData.wrapper = context.defaultWrapper.object;
0717: mappingData.requestPath.setChars(path
0718: .getBuffer(), path.getStart(),
0719: path.getLength());
0720: mappingData.wrapperPath.setChars(path
0721: .getBuffer(), path.getStart(),
0722: path.getLength());
0723: mappingData.requestPath
0724: .setString(pathStr);
0725: mappingData.wrapperPath
0726: .setString(pathStr);
0727: }
0728: }
0729: }
0730: }
0731: path.setOffset(servletPath);
0732: path.setEnd(pathEnd);
0733: }
0734: }
0735:
0736: // Rule 7 -- Default servlet
0737: if (mappingData.wrapper == null && !checkJspWelcomeFiles) {
0738: if (context.defaultWrapper != null) {
0739: mappingData.wrapper = context.defaultWrapper.object;
0740: mappingData.requestPath.setChars(path.getBuffer(), path
0741: .getStart(), path.getLength());
0742: mappingData.wrapperPath.setChars(path.getBuffer(), path
0743: .getStart(), path.getLength());
0744: }
0745: // Redirection to a folder
0746: char[] buf = path.getBuffer();
0747: if (context.resources != null && buf[pathEnd - 1] != '/') {
0748: Object file = null;
0749: String pathStr = path.toString();
0750: try {
0751: file = context.resources.lookup(pathStr);
0752: } catch (NamingException nex) {
0753: // Swallow, since someone else handles the 404
0754: }
0755: if (file != null && file instanceof DirContext) {
0756: // Note: this mutates the path: do not do any processing
0757: // after this (since we set the redirectPath, there
0758: // shouldn't be any)
0759: path.setOffset(pathOffset);
0760: path.append('/');
0761: mappingData.redirectPath.setChars(path.getBuffer(),
0762: path.getStart(), path.getLength());
0763: } else {
0764: mappingData.requestPath.setString(pathStr);
0765: mappingData.wrapperPath.setString(pathStr);
0766: }
0767: }
0768: }
0769:
0770: path.setOffset(pathOffset);
0771: path.setEnd(pathEnd);
0772:
0773: }
0774:
0775: /**
0776: * Exact mapping.
0777: */
0778: private final void internalMapExactWrapper(Wrapper[] wrappers,
0779: CharChunk path, MappingData mappingData) {
0780: int pos = find(wrappers, path);
0781: if ((pos != -1) && (path.equals(wrappers[pos].name))) {
0782: mappingData.requestPath.setString(wrappers[pos].name);
0783: mappingData.wrapperPath.setString(wrappers[pos].name);
0784: mappingData.wrapper = wrappers[pos].object;
0785: }
0786: }
0787:
0788: /**
0789: * Wildcard mapping.
0790: */
0791: private final void internalMapWildcardWrapper(Wrapper[] wrappers,
0792: int nesting, CharChunk path, MappingData mappingData) {
0793:
0794: int pathEnd = path.getEnd();
0795: int pathOffset = path.getOffset();
0796:
0797: int lastSlash = -1;
0798: int length = -1;
0799: int pos = find(wrappers, path);
0800: if (pos != -1) {
0801: boolean found = false;
0802: while (pos >= 0) {
0803: if (path.startsWith(wrappers[pos].name)) {
0804: length = wrappers[pos].name.length();
0805: if (path.getLength() == length) {
0806: found = true;
0807: break;
0808: } else if (path.startsWithIgnoreCase("/", length)) {
0809: found = true;
0810: break;
0811: }
0812: }
0813: if (lastSlash == -1) {
0814: lastSlash = nthSlash(path, nesting + 1);
0815: } else {
0816: lastSlash = lastSlash(path);
0817: }
0818: path.setEnd(lastSlash);
0819: pos = find(wrappers, path);
0820: }
0821: path.setEnd(pathEnd);
0822: if (found) {
0823: mappingData.wrapperPath.setString(wrappers[pos].name);
0824: if (path.getLength() > length) {
0825: mappingData.pathInfo.setChars(path.getBuffer(),
0826: path.getOffset() + length, path.getLength()
0827: - length);
0828: }
0829: mappingData.requestPath.setChars(path.getBuffer(), path
0830: .getOffset(), path.getLength());
0831: mappingData.wrapper = wrappers[pos].object;
0832: mappingData.jspWildCard = wrappers[pos].jspWildCard;
0833: }
0834: }
0835: }
0836:
0837: /**
0838: * Extension mappings.
0839: */
0840: private final void internalMapExtensionWrapper(Wrapper[] wrappers,
0841: CharChunk path, MappingData mappingData) {
0842: char[] buf = path.getBuffer();
0843: int pathEnd = path.getEnd();
0844: int servletPath = path.getOffset();
0845: int slash = -1;
0846: for (int i = pathEnd - 1; i >= servletPath; i--) {
0847: if (buf[i] == '/') {
0848: slash = i;
0849: break;
0850: }
0851: }
0852: if (slash >= 0) {
0853: int period = -1;
0854: for (int i = pathEnd - 1; i > slash; i--) {
0855: if (buf[i] == '.') {
0856: period = i;
0857: break;
0858: }
0859: }
0860: if (period >= 0) {
0861: path.setOffset(period + 1);
0862: path.setEnd(pathEnd);
0863: int pos = find(wrappers, path);
0864: if ((pos != -1) && (path.equals(wrappers[pos].name))) {
0865: mappingData.wrapperPath.setChars(buf, servletPath,
0866: pathEnd - servletPath);
0867: mappingData.requestPath.setChars(buf, servletPath,
0868: pathEnd - servletPath);
0869: mappingData.wrapper = wrappers[pos].object;
0870: }
0871: path.setOffset(servletPath);
0872: path.setEnd(pathEnd);
0873: }
0874: }
0875: }
0876:
0877: /**
0878: * Find a map elemnt given its name in a sorted array of map elements.
0879: * This will return the index for the closest inferior or equal item in the
0880: * given array.
0881: */
0882: private static final int find(MapElement[] map, CharChunk name) {
0883: return find(map, name, name.getStart(), name.getEnd());
0884: }
0885:
0886: /**
0887: * Find a map elemnt given its name in a sorted array of map elements.
0888: * This will return the index for the closest inferior or equal item in the
0889: * given array.
0890: */
0891: private static final int find(MapElement[] map, CharChunk name,
0892: int start, int end) {
0893:
0894: int a = 0;
0895: int b = map.length - 1;
0896:
0897: // Special cases: -1 and 0
0898: if (b == -1) {
0899: return -1;
0900: }
0901:
0902: if (compare(name, start, end, map[0].name) < 0) {
0903: return -1;
0904: }
0905: if (b == 0) {
0906: return 0;
0907: }
0908:
0909: int i = 0;
0910: while (true) {
0911: i = (b + a) / 2;
0912: int result = compare(name, start, end, map[i].name);
0913: if (result == 1) {
0914: a = i;
0915: } else if (result == 0) {
0916: return i;
0917: } else {
0918: b = i;
0919: }
0920: if ((b - a) == 1) {
0921: int result2 = compare(name, start, end, map[b].name);
0922: if (result2 < 0) {
0923: return a;
0924: } else {
0925: return b;
0926: }
0927: }
0928: }
0929:
0930: }
0931:
0932: /**
0933: * Find a map elemnt given its name in a sorted array of map elements.
0934: * This will return the index for the closest inferior or equal item in the
0935: * given array.
0936: */
0937: private static final int find(MapElement[] map, String name) {
0938:
0939: int a = 0;
0940: int b = map.length - 1;
0941:
0942: // Special cases: -1 and 0
0943: if (b == -1) {
0944: return -1;
0945: }
0946:
0947: if (name.compareTo(map[0].name) < 0) {
0948: return -1;
0949: }
0950: if (b == 0) {
0951: return 0;
0952: }
0953:
0954: int i = 0;
0955: while (true) {
0956: i = (b + a) / 2;
0957: int result = name.compareTo(map[i].name);
0958: if (result > 0) {
0959: a = i;
0960: } else if (result == 0) {
0961: return i;
0962: } else {
0963: b = i;
0964: }
0965: if ((b - a) == 1) {
0966: int result2 = name.compareTo(map[b].name);
0967: if (result2 < 0) {
0968: return a;
0969: } else {
0970: return b;
0971: }
0972: }
0973: }
0974:
0975: }
0976:
0977: /**
0978: * Compare given char chunk with String.
0979: * Return -1, 0 or +1 if inferior, equal, or superior to the String.
0980: */
0981: private static final int compare(CharChunk name, int start,
0982: int end, String compareTo) {
0983: int result = 0;
0984: char[] c = name.getBuffer();
0985: int len = compareTo.length();
0986: if ((end - start) < len) {
0987: len = end - start;
0988: }
0989: for (int i = 0; (i < len) && (result == 0); i++) {
0990: if (c[i + start] > compareTo.charAt(i)) {
0991: result = 1;
0992: } else if (c[i + start] < compareTo.charAt(i)) {
0993: result = -1;
0994: }
0995: }
0996: if (result == 0) {
0997: if (compareTo.length() > (end - start)) {
0998: result = -1;
0999: } else if (compareTo.length() < (end - start)) {
1000: result = 1;
1001: }
1002: }
1003: return result;
1004: }
1005:
1006: /**
1007: * Find the position of the last slash in the given char chunk.
1008: */
1009: private static final int lastSlash(CharChunk name) {
1010:
1011: char[] c = name.getBuffer();
1012: int end = name.getEnd();
1013: int start = name.getStart();
1014: int pos = end;
1015:
1016: while (pos > start) {
1017: if (c[--pos] == '/') {
1018: break;
1019: }
1020: }
1021:
1022: return (pos);
1023:
1024: }
1025:
1026: /**
1027: * Find the position of the nth slash, in the given char chunk.
1028: */
1029: private static final int nthSlash(CharChunk name, int n) {
1030:
1031: char[] c = name.getBuffer();
1032: int end = name.getEnd();
1033: int start = name.getStart();
1034: int pos = start;
1035: int count = 0;
1036:
1037: while (pos < end) {
1038: if ((c[pos++] == '/') && ((++count) == n)) {
1039: pos--;
1040: break;
1041: }
1042: }
1043:
1044: return (pos);
1045:
1046: }
1047:
1048: /**
1049: * Return the slash count in a given string.
1050: */
1051: private static final int slashCount(String name) {
1052: int pos = -1;
1053: int count = 0;
1054: while ((pos = name.indexOf('/', pos + 1)) != -1) {
1055: count++;
1056: }
1057: return count;
1058: }
1059:
1060: /**
1061: * Insert into the right place in a sorted MapElement array, and prevent
1062: * duplicates.
1063: */
1064: private static final boolean insertMap(MapElement[] oldMap,
1065: MapElement[] newMap, MapElement newElement) {
1066: int pos = find(oldMap, newElement.name);
1067: if ((pos != -1) && (newElement.name.equals(oldMap[pos].name))) {
1068: return false;
1069: }
1070: System.arraycopy(oldMap, 0, newMap, 0, pos + 1);
1071: newMap[pos + 1] = newElement;
1072: System.arraycopy(oldMap, pos + 1, newMap, pos + 2,
1073: oldMap.length - pos - 1);
1074: return true;
1075: }
1076:
1077: /**
1078: * Insert into the right place in a sorted MapElement array.
1079: */
1080: private static final boolean removeMap(MapElement[] oldMap,
1081: MapElement[] newMap, String name) {
1082: int pos = find(oldMap, name);
1083: if ((pos != -1) && (name.equals(oldMap[pos].name))) {
1084: System.arraycopy(oldMap, 0, newMap, 0, pos);
1085: System.arraycopy(oldMap, pos + 1, newMap, pos,
1086: oldMap.length - pos - 1);
1087: return true;
1088: }
1089: return false;
1090: }
1091:
1092: // ------------------------------------------------- MapElement Inner Class
1093:
1094: protected static abstract class MapElement {
1095:
1096: public String name = null;
1097: public Object object = null;
1098:
1099: }
1100:
1101: // ------------------------------------------------------- Host Inner Class
1102:
1103: protected static final class Host extends MapElement {
1104:
1105: public ContextList contextList = null;
1106:
1107: }
1108:
1109: // ------------------------------------------------ ContextList Inner Class
1110:
1111: protected static final class ContextList {
1112:
1113: public Context[] contexts = new Context[0];
1114: public int nesting = 0;
1115:
1116: }
1117:
1118: // ---------------------------------------------------- Context Inner Class
1119:
1120: protected static final class Context extends MapElement {
1121:
1122: public String path = null;
1123: public String[] welcomeResources = new String[0];
1124: public javax.naming.Context resources = null;
1125: public Wrapper defaultWrapper = null;
1126: public Wrapper[] exactWrappers = new Wrapper[0];
1127: public Wrapper[] wildcardWrappers = new Wrapper[0];
1128: public Wrapper[] extensionWrappers = new Wrapper[0];
1129: public int nesting = 0;
1130:
1131: }
1132:
1133: // ---------------------------------------------------- Wrapper Inner Class
1134:
1135: protected static class Wrapper extends MapElement {
1136:
1137: public String path = null;
1138: public boolean jspWildCard = false;
1139: }
1140:
1141: // -------------------------------------------------------- Testing Methods
1142:
1143: public static void main(String args[]) {
1144:
1145: try {
1146:
1147: Mapper mapper = new Mapper();
1148: System.out.println("Start");
1149:
1150: mapper.addHost("sjbjdvwsbvhrb", new String[0], "blah1");
1151: mapper.addHost("sjbjdvwsbvhr/", new String[0], "blah1");
1152: mapper.addHost("wekhfewuifweuibf", new String[0], "blah2");
1153: mapper.addHost("ylwrehirkuewh", new String[0], "blah3");
1154: mapper.addHost("iohgeoihro", new String[0], "blah4");
1155: mapper.addHost("fwehoihoihwfeo", new String[0], "blah5");
1156: mapper.addHost("owefojiwefoi", new String[0], "blah6");
1157: mapper.addHost("iowejoiejfoiew", new String[0], "blah7");
1158: mapper.addHost("iowejoiejfoiew", new String[0], "blah17");
1159: mapper.addHost("ohewoihfewoih", new String[0], "blah8");
1160: mapper.addHost("fewohfoweoih", new String[0], "blah9");
1161: mapper.addHost("ttthtiuhwoih", new String[0], "blah10");
1162: mapper.addHost("lkwefjwojweffewoih", new String[0],
1163: "blah11");
1164: mapper.addHost("zzzuyopjvewpovewjhfewoih", new String[0],
1165: "blah12");
1166: mapper.addHost("xxxxgqwiwoih", new String[0], "blah13");
1167: mapper.addHost("qwigqwiwoih", new String[0], "blah14");
1168:
1169: System.out.println("Map:");
1170: for (int i = 0; i < mapper.hosts.length; i++) {
1171: System.out.println(mapper.hosts[i].name);
1172: }
1173:
1174: mapper.setDefaultHostName("ylwrehirkuewh");
1175:
1176: String[] welcomes = new String[2];
1177: welcomes[0] = "boo/baba";
1178: welcomes[1] = "bobou";
1179:
1180: mapper.addContext("iowejoiejfoiew", "", "context0",
1181: new String[0], null);
1182: mapper.addContext("iowejoiejfoiew", "/foo", "context1",
1183: new String[0], null);
1184: mapper.addContext("iowejoiejfoiew", "/foo/bar", "context2",
1185: welcomes, null);
1186: mapper.addContext("iowejoiejfoiew", "/foo/bar/bla",
1187: "context3", new String[0], null);
1188:
1189: mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "/fo/*",
1190: "wrapper0");
1191: mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "/",
1192: "wrapper1");
1193: mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "/blh",
1194: "wrapper2");
1195: mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "*.jsp",
1196: "wrapper3");
1197: mapper.addWrapper("iowejoiejfoiew", "/foo/bar",
1198: "/blah/bou/*", "wrapper4");
1199: mapper.addWrapper("iowejoiejfoiew", "/foo/bar",
1200: "/blah/bobou/*", "wrapper5");
1201: mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "*.htm",
1202: "wrapper6");
1203:
1204: MappingData mappingData = new MappingData();
1205: MessageBytes host = MessageBytes.newInstance();
1206: host.setString("iowejoiejfoiew");
1207: MessageBytes uri = MessageBytes.newInstance();
1208: uri.setString("/foo/bar/blah/bobou/foo");
1209: uri.toChars();
1210: uri.getCharChunk().setLimit(-1);
1211:
1212: mapper.map(host, uri, mappingData);
1213: System.out.println("MD Host:" + mappingData.host);
1214: System.out.println("MD Context:" + mappingData.context);
1215: System.out.println("MD Wrapper:" + mappingData.wrapper);
1216:
1217: System.out
1218: .println("contextPath:" + mappingData.contextPath);
1219: System.out
1220: .println("wrapperPath:" + mappingData.wrapperPath);
1221: System.out.println("pathInfo:" + mappingData.pathInfo);
1222: System.out.println("redirectPath:"
1223: + mappingData.redirectPath);
1224:
1225: mappingData.recycle();
1226: mapper.map(host, uri, mappingData);
1227: System.out.println("MD Host:" + mappingData.host);
1228: System.out.println("MD Context:" + mappingData.context);
1229: System.out.println("MD Wrapper:" + mappingData.wrapper);
1230:
1231: System.out
1232: .println("contextPath:" + mappingData.contextPath);
1233: System.out
1234: .println("wrapperPath:" + mappingData.wrapperPath);
1235: System.out.println("pathInfo:" + mappingData.pathInfo);
1236: System.out.println("redirectPath:"
1237: + mappingData.redirectPath);
1238:
1239: for (int i = 0; i < 1000000; i++) {
1240: mappingData.recycle();
1241: mapper.map(host, uri, mappingData);
1242: }
1243:
1244: long time = System.currentTimeMillis();
1245: for (int i = 0; i < 1000000; i++) {
1246: mappingData.recycle();
1247: mapper.map(host, uri, mappingData);
1248: }
1249: System.out.println("Elapsed:"
1250: + (System.currentTimeMillis() - time));
1251:
1252: System.out.println("MD Host:" + mappingData.host);
1253: System.out.println("MD Context:" + mappingData.context);
1254: System.out.println("MD Wrapper:" + mappingData.wrapper);
1255:
1256: System.out
1257: .println("contextPath:" + mappingData.contextPath);
1258: System.out
1259: .println("wrapperPath:" + mappingData.wrapperPath);
1260: System.out
1261: .println("requestPath:" + mappingData.requestPath);
1262: System.out.println("pathInfo:" + mappingData.pathInfo);
1263: System.out.println("redirectPath:"
1264: + mappingData.redirectPath);
1265:
1266: } catch (Exception e) {
1267: e.printStackTrace();
1268: }
1269:
1270: }
1271:
1272: }
|