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