Return-Path: Delivered-To: apmail-tomcat-dev-archive@www.apache.org Received: (qmail 63622 invoked from network); 21 Aug 2006 15:22:43 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 21 Aug 2006 15:22:42 -0000 Received: (qmail 62081 invoked by uid 500); 21 Aug 2006 15:21:25 -0000 Delivered-To: apmail-tomcat-dev-archive@tomcat.apache.org Received: (qmail 61930 invoked by uid 500); 21 Aug 2006 15:21:24 -0000 Mailing-List: contact dev-help@tomcat.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: "Tomcat Developers List" Delivered-To: mailing list dev@tomcat.apache.org Received: (qmail 61835 invoked by uid 500); 21 Aug 2006 15:21:24 -0000 Delivered-To: apmail-jakarta-tomcat-dev@jakarta.apache.org Received: (qmail 61652 invoked by uid 99); 21 Aug 2006 15:21:23 -0000 Received: from asf.osuosl.org (HELO asf.osuosl.org) (140.211.166.49) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 21 Aug 2006 08:21:23 -0700 X-ASF-Spam-Status: No, hits=-9.4 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received-SPF: pass (asf.osuosl.org: local policy) Received: from [140.211.166.113] (HELO eris.apache.org) (140.211.166.113) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 21 Aug 2006 08:21:17 -0700 Received: by eris.apache.org (Postfix, from userid 65534) id 7CEC61A9823; Mon, 21 Aug 2006 08:20:56 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r433260 [4/5] - in /tomcat/sandbox/tomcat-lite: ./ java/org/apache/coyote/ java/org/apache/tomcat/lite/ java/org/apache/tomcat/lite/ctxmap/ java/org/apache/tomcat/lite/http/ java/org/apache/tomcat/lite/jmx/ java/org/apache/tomcat/lite/servl... Date: Mon, 21 Aug 2006 15:20:46 -0000 To: tomcat-dev@jakarta.apache.org From: costin@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20060821152056.7CEC61A9823@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N Added: tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/lite/webmap/WebappServletMapper.java URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/lite/webmap/WebappServletMapper.java?rev=433260&view=auto ============================================================================== --- tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/lite/webmap/WebappServletMapper.java (added) +++ tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/lite/webmap/WebappServletMapper.java Mon Aug 21 08:20:40 2006 @@ -0,0 +1,854 @@ +/* + * Copyright 1999-2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.tomcat.lite.webmap; + +import java.io.File; + +import org.apache.tomcat.lite.ServletContextImpl; +import org.apache.tomcat.lite.util.MappingData; +import org.apache.tomcat.util.buf.Ascii; +import org.apache.tomcat.util.buf.CharChunk; +import org.apache.tomcat.util.buf.MessageBytes; + +/** + * Mapper, which implements the servlet API mapping rules (which are derived + * from the HTTP rules). + * + * Based on catalina mapper - but simplified. All host and context mappings + * is done in HostMapper - this is just dealing with web.xml. + * + * For corner cases ( very large number of rules, dynamic rules, etc ) you + * can override the mapper for a context with a class extending this. + */ +public class WebappServletMapper { + + /** + * Context associated with this wrapper, used for wrapper mapping. + */ + public ContextMapElement contextMapElement = new ContextMapElement(); + + + // --------------------------------------------------------- Public Methods + + public WebappServletMapper(ServletContextImpl impl) { + contextMapElement.object = impl; + contextMapElement.name = impl.getContextPath(); + } + + + /** Set context, used for wrapper mapping (request dispatcher). + * + * @param welcomeResources Welcome files defined for this context + * @param resources Static resources of the context + */ + public void setContext(String path, String[] welcomeResources, + File resources) { + contextMapElement.name = path; + contextMapElement.welcomeResources = welcomeResources; + contextMapElement.resources = resources; + } + + + /** + * Add a wrapper to the context associated with this wrapper. + * + * @param path Wrapper mapping + * @param wrapper The Wrapper object + */ + public void addWrapper(String path, Object wrapper) { + addWrapper(contextMapElement, path, wrapper); + } + + + public void addWrapper(String path, Object wrapper, boolean jspWildCard) { + addWrapper(contextMapElement, path, wrapper, jspWildCard); + } + + + public void addWrapper(ContextMapElement context, String path, Object wrapper) { + addWrapper(context, path, wrapper, false); + } + + + /** + * Adds a wrapper to the given context. + * + * @param context The context to which to add the wrapper + * @param path Wrapper mapping + * @param wrapper The Wrapper object + * @param jspWildCard true if the wrapper corresponds to the JspServlet + * and the mapping path contains a wildcard; false otherwise + */ + protected void addWrapper(ContextMapElement context, String path, Object wrapper, + boolean jspWildCard) { + + synchronized (context) { + WrapperMapElement newWrapper = new WrapperMapElement(); + newWrapper.object = wrapper; + newWrapper.jspWildCard = jspWildCard; + if (path.endsWith("/*")) { + // Wildcard wrapper + newWrapper.name = path.substring(0, path.length() - 2); + WrapperMapElement[] oldWrappers = context.wildcardWrappers; + WrapperMapElement[] newWrappers = + new WrapperMapElement[oldWrappers.length + 1]; + if (insertMap(oldWrappers, newWrappers, newWrapper)) { + context.wildcardWrappers = newWrappers; + int slashCount = slashCount(newWrapper.name); + if (slashCount > context.nesting) { + context.nesting = slashCount; + } + } + } else if (path.startsWith("*.")) { + // Extension wrapper + newWrapper.name = path.substring(2); + WrapperMapElement[] oldWrappers = context.extensionWrappers; + WrapperMapElement[] newWrappers = + new WrapperMapElement[oldWrappers.length + 1]; + if (insertMap(oldWrappers, newWrappers, newWrapper)) { + context.extensionWrappers = newWrappers; + } + } else if (path.equals("/")) { + // Default wrapper + newWrapper.name = ""; + context.defaultWrapper = newWrapper; + } else { + // Exact wrapper + newWrapper.name = path; + WrapperMapElement[] oldWrappers = context.exactWrappers; + WrapperMapElement[] newWrappers = + new WrapperMapElement[oldWrappers.length + 1]; + if (insertMap(oldWrappers, newWrappers, newWrapper)) { + context.exactWrappers = newWrappers; + } + } + } + } + + + /** + * Remove a wrapper from the context associated with this wrapper. + * + * @param path Wrapper mapping + */ + public void removeWrapper(String path) { + removeWrapper(contextMapElement, path); + } + + + protected void removeWrapper(ContextMapElement context, String path) { + synchronized (context) { + if (path.endsWith("/*")) { + // Wildcard wrapper + String name = path.substring(0, path.length() - 2); + WrapperMapElement[] oldWrappers = context.wildcardWrappers; + WrapperMapElement[] newWrappers = + new WrapperMapElement[oldWrappers.length - 1]; + if (removeMap(oldWrappers, newWrappers, name)) { + // Recalculate nesting + context.nesting = 0; + for (int i = 0; i < newWrappers.length; i++) { + int slashCount = slashCount(newWrappers[i].name); + if (slashCount > context.nesting) { + context.nesting = slashCount; + } + } + context.wildcardWrappers = newWrappers; + } + } else if (path.startsWith("*.")) { + // Extension wrapper + String name = path.substring(2); + WrapperMapElement[] oldWrappers = context.extensionWrappers; + WrapperMapElement[] newWrappers = + new WrapperMapElement[oldWrappers.length - 1]; + if (removeMap(oldWrappers, newWrappers, name)) { + context.extensionWrappers = newWrappers; + } + } else if (path.equals("/")) { + // Default wrapper + context.defaultWrapper = null; + } else { + // Exact wrapper + String name = path; + WrapperMapElement[] oldWrappers = context.exactWrappers; + WrapperMapElement[] newWrappers = + new WrapperMapElement[oldWrappers.length - 1]; + if (removeMap(oldWrappers, newWrappers, name)) { + context.exactWrappers = newWrappers; + } + } + } + } + + /** + * Map the specified URI relative to the context, + * mutating the given mapping data. + * + * @param uri URI + * @param mappingData This structure will contain the result of the mapping + * operation + */ + public void map(MessageBytes uri, MappingData mappingData) + throws Exception { + + uri.toChars(); + CharChunk uricc = uri.getCharChunk(); + uricc.setLimit(-1); + internalMapWrapper(contextMapElement, uricc, mappingData); + + } + + + // -------------------------------------------------------- Private Methods + + + /** + * Wrapper mapping. + */ + private final void internalMapWrapper(ContextMapElement context, CharChunk path, + MappingData mappingData) + throws Exception { + + int pathOffset = path.getOffset(); + int pathEnd = path.getEnd(); + int servletPath = pathOffset; + boolean noServletPath = false; + + int length = context.name.length(); + if (length != (pathEnd - pathOffset)) { + servletPath = pathOffset + length; + } else { + noServletPath = true; + path.append('/'); + pathOffset = path.getOffset(); + pathEnd = path.getEnd(); + servletPath = pathOffset+length; + } + + path.setOffset(servletPath); + + // Rule 1 -- Exact Match + WrapperMapElement[] exactWrappers = context.exactWrappers; + internalMapExactWrapper(exactWrappers, path, mappingData); + + // Rule 2 -- Prefix Match + boolean checkJspWelcomeFiles = false; + WrapperMapElement[] wildcardWrappers = context.wildcardWrappers; + if (mappingData.wrapper == null) { + internalMapWildcardWrapper(wildcardWrappers, context.nesting, + path, mappingData); + if (mappingData.wrapper != null && mappingData.jspWildCard) { + char[] buf = path.getBuffer(); + if (buf[pathEnd - 1] == '/') { + /* + * Path ending in '/' was mapped to JSP servlet based on + * wildcard match (e.g., as specified in url-pattern of a + * jsp-property-group. + * Force the context's welcome files, which are interpreted + * as JSP files (since they match the url-pattern), to be + * considered. See Bugzilla 27664. + */ + mappingData.wrapper = null; + checkJspWelcomeFiles = true; + } else { + // See Bugzilla 27704 + mappingData.wrapperPath.setChars(buf, path.getStart(), + path.getLength()); + mappingData.pathInfo.recycle(); + } + } + } + + if(mappingData.wrapper == null && noServletPath) { + // The path is empty, redirect to "/" + mappingData.redirectPath.setChars + (path.getBuffer(), pathOffset, pathEnd); + path.setEnd(pathEnd - 1); + //return; + } + + // Rule 3 -- Extension Match + WrapperMapElement[] extensionWrappers = context.extensionWrappers; + if (mappingData.wrapper == null && !checkJspWelcomeFiles) { + internalMapExtensionWrapper(extensionWrappers, path, mappingData); + } + + File file = null; + // Rule 4 -- Welcome resources processing for servlets + if (mappingData.wrapper == null) { + boolean checkWelcomeFiles = checkJspWelcomeFiles; + if (!checkWelcomeFiles) { + char[] buf = path.getBuffer(); + checkWelcomeFiles = (buf[pathEnd - 1] == '/'); + } + if (checkWelcomeFiles) { + for (int i = 0; (i < context.welcomeResources.length) + && (mappingData.wrapper == null); i++) { + path.setOffset(pathOffset); + path.setEnd(pathEnd); + path.append(context.welcomeResources[i], 0, + context.welcomeResources[i].length()); + path.setOffset(servletPath); + + // Rule 4a -- Welcome resources processing for exact macth + internalMapExactWrapper(exactWrappers, path, mappingData); + + // Rule 4b -- Welcome resources processing for prefix match + if (mappingData.wrapper == null) { + internalMapWildcardWrapper + (wildcardWrappers, context.nesting, + path, mappingData); + } + + // Rule 4c -- Welcome resources processing + // for physical folder + if (mappingData.wrapper == null + && context.resources != null) { + // Default servlet: check if it's file or dir to apply + // welcome files rules. + // TODO: Save the File in attributes, + // to avoid duplication in DefaultServlet. + + String pathStr = path.toString(); + file = new File(context.resources, pathStr); + if (file.exists() && !(file.isDirectory()) ) { + + internalMapExtensionWrapper(extensionWrappers, + path, mappingData); + if (mappingData.wrapper == null + && context.defaultWrapper != null) { + mappingData.wrapper = + context.defaultWrapper.object; + mappingData.requestPath.setChars + (path.getBuffer(), path.getStart(), + path.getLength()); + mappingData.wrapperPath.setChars + (path.getBuffer(), path.getStart(), + path.getLength()); + mappingData.requestPath.setString(pathStr); + mappingData.wrapperPath.setString(pathStr); + } + } + } + } + + path.setOffset(servletPath); + path.setEnd(pathEnd); + } + + } + + + // Rule 7 -- Default servlet + if (mappingData.wrapper == null && !checkJspWelcomeFiles) { + if (context.defaultWrapper != null) { + mappingData.wrapper = context.defaultWrapper.object; + mappingData.requestPath.setChars + (path.getBuffer(), path.getStart(), path.getLength()); + mappingData.wrapperPath.setChars + (path.getBuffer(), path.getStart(), path.getLength()); + } + // Redirection to a folder + char[] buf = path.getBuffer(); + if (context.resources != null && buf[pathEnd -1 ] != '/') { + String pathStr = path.toString(); + file = new File( context.resources, pathStr); + if (file.exists() && file.isDirectory()) { + // Note: this mutates the path: do not do any processing + // after this (since we set the redirectPath, there + // shouldn't be any) + path.setOffset(pathOffset); + path.append('/'); + mappingData.redirectPath.setChars + (path.getBuffer(), path.getStart(), path.getLength()); + } else { + mappingData.requestPath.setString(pathStr); + mappingData.wrapperPath.setString(pathStr); + } + } + } + + path.setOffset(pathOffset); + path.setEnd(pathEnd); + + } + + + /** + * Exact mapping. + */ + private final void internalMapExactWrapper + (WrapperMapElement[] wrappers, CharChunk path, MappingData mappingData) { + int pos = find(wrappers, path); + if ((pos != -1) && (path.equals(wrappers[pos].name))) { + mappingData.requestPath.setString(wrappers[pos].name); + mappingData.wrapperPath.setString(wrappers[pos].name); + mappingData.wrapper = wrappers[pos].object; + } + } + + + /** + * Wildcard mapping. + */ + private final void internalMapWildcardWrapper + (WrapperMapElement[] wrappers, int nesting, CharChunk path, + MappingData mappingData) { + + int pathEnd = path.getEnd(); + int pathOffset = path.getOffset(); + + int lastSlash = -1; + int length = -1; + int pos = find(wrappers, path); + if (pos != -1) { + boolean found = false; + while (pos >= 0) { + if (path.startsWith(wrappers[pos].name)) { + length = wrappers[pos].name.length(); + if (path.getLength() == length) { + found = true; + break; + } else if (path.startsWithIgnoreCase("/", length)) { + found = true; + break; + } + } + if (lastSlash == -1) { + lastSlash = nthSlash(path, nesting + 1); + } else { + lastSlash = lastSlash(path); + } + path.setEnd(lastSlash); + pos = find(wrappers, path); + } + path.setEnd(pathEnd); + if (found) { + mappingData.wrapperPath.setString(wrappers[pos].name); + if (path.getLength() > length) { + mappingData.pathInfo.setChars + (path.getBuffer(), + path.getOffset() + length, + path.getLength() - length); + } + mappingData.requestPath.setChars + (path.getBuffer(), path.getOffset(), path.getLength()); + mappingData.wrapper = wrappers[pos].object; + mappingData.jspWildCard = wrappers[pos].jspWildCard; + } + } + } + + + /** + * Extension mappings. + */ + private final void internalMapExtensionWrapper + (WrapperMapElement[] wrappers, CharChunk path, MappingData mappingData) { + char[] buf = path.getBuffer(); + int pathEnd = path.getEnd(); + int servletPath = path.getOffset(); + int slash = -1; + for (int i = pathEnd - 1; i >= servletPath; i--) { + if (buf[i] == '/') { + slash = i; + break; + } + } + if (slash >= 0) { + int period = -1; + for (int i = pathEnd - 1; i > slash; i--) { + if (buf[i] == '.') { + period = i; + break; + } + } + if (period >= 0) { + path.setOffset(period + 1); + path.setEnd(pathEnd); + int pos = find(wrappers, path); + if ((pos != -1) + && (path.equals(wrappers[pos].name))) { + mappingData.wrapperPath.setChars + (buf, servletPath, pathEnd - servletPath); + mappingData.requestPath.setChars + (buf, servletPath, pathEnd - servletPath); + mappingData.wrapper = wrappers[pos].object; + } + path.setOffset(servletPath); + path.setEnd(pathEnd); + } + } + } + + + /** + * Find a map elemnt given its name in a sorted array of map elements. + * This will return the index for the closest inferior or equal item in the + * given array. + */ + public static final int find(MapElement[] map, CharChunk name) { + return find(map, name, name.getStart(), name.getEnd()); + } + + + /** + * Find a map elemnt given its name in a sorted array of map elements. + * This will return the index for the closest inferior or equal item in the + * given array. + */ + private static final int find(MapElement[] map, CharChunk name, + int start, int end) { + + int a = 0; + int b = map.length - 1; + + // Special cases: -1 and 0 + if (b == -1) { + return -1; + } + + if (compare(name, start, end, map[0].name) < 0 ) { + return -1; + } + if (b == 0) { + return 0; + } + + int i = 0; + while (true) { + i = (b + a) / 2; + int result = compare(name, start, end, map[i].name); + if (result == 1) { + a = i; + } else if (result == 0) { + return i; + } else { + b = i; + } + if ((b - a) == 1) { + int result2 = compare(name, start, end, map[b].name); + if (result2 < 0) { + return a; + } else { + return b; + } + } + } + + } + + /** + * Find a map elemnt given its name in a sorted array of map elements. + * This will return the index for the closest inferior or equal item in the + * given array. + */ + private static final int findIgnoreCase(MapElement[] map, CharChunk name) { + return findIgnoreCase(map, name, name.getStart(), name.getEnd()); + } + + + /** + * Find a map elemnt given its name in a sorted array of map elements. + * This will return the index for the closest inferior or equal item in the + * given array. + */ + private static final int findIgnoreCase(MapElement[] map, CharChunk name, + int start, int end) { + + int a = 0; + int b = map.length - 1; + + // Special cases: -1 and 0 + if (b == -1) { + return -1; + } + if (compareIgnoreCase(name, start, end, map[0].name) < 0 ) { + return -1; + } + if (b == 0) { + return 0; + } + + int i = 0; + while (true) { + i = (b + a) / 2; + int result = compareIgnoreCase(name, start, end, map[i].name); + if (result == 1) { + a = i; + } else if (result == 0) { + return i; + } else { + b = i; + } + if ((b - a) == 1) { + int result2 = compareIgnoreCase(name, start, end, map[b].name); + if (result2 < 0) { + return a; + } else { + return b; + } + } + } + + } + + + /** + * Find a map elemnt given its name in a sorted array of map elements. + * This will return the index for the closest inferior or equal item in the + * given array. + */ + public static final int find(MapElement[] map, String name) { + + int a = 0; + int b = map.length - 1; + + // Special cases: -1 and 0 + if (b == -1) { + return -1; + } + + if (name.compareTo(map[0].name) < 0) { + return -1; + } + if (b == 0) { + return 0; + } + + int i = 0; + while (true) { + i = (b + a) / 2; + int result = name.compareTo(map[i].name); + if (result > 0) { + a = i; + } else if (result == 0) { + return i; + } else { + b = i; + } + if ((b - a) == 1) { + int result2 = name.compareTo(map[b].name); + if (result2 < 0) { + return a; + } else { + return b; + } + } + } + + } + + + /** + * Compare given char chunk with String. + * Return -1, 0 or +1 if inferior, equal, or superior to the String. + */ + private static final int compare(CharChunk name, int start, int end, + String compareTo) { + int result = 0; + char[] c = name.getBuffer(); + int len = compareTo.length(); + if ((end - start) < len) { + len = end - start; + } + for (int i = 0; (i < len) && (result == 0); i++) { + if (c[i + start] > compareTo.charAt(i)) { + result = 1; + } else if (c[i + start] < compareTo.charAt(i)) { + result = -1; + } + } + if (result == 0) { + if (compareTo.length() > (end - start)) { + result = -1; + } else if (compareTo.length() < (end - start)) { + result = 1; + } + } + return result; + } + + + /** + * Compare given char chunk with String ignoring case. + * Return -1, 0 or +1 if inferior, equal, or superior to the String. + */ + private static final int compareIgnoreCase(CharChunk name, int start, int end, + String compareTo) { + int result = 0; + char[] c = name.getBuffer(); + int len = compareTo.length(); + if ((end - start) < len) { + len = end - start; + } + for (int i = 0; (i < len) && (result == 0); i++) { + if (Ascii.toLower(c[i + start]) > Ascii.toLower(compareTo.charAt(i))) { + result = 1; + } else if (Ascii.toLower(c[i + start]) < Ascii.toLower(compareTo.charAt(i))) { + result = -1; + } + } + if (result == 0) { + if (compareTo.length() > (end - start)) { + result = -1; + } else if (compareTo.length() < (end - start)) { + result = 1; + } + } + return result; + } + + + /** + * Find the position of the last slash in the given char chunk. + */ + public static final int lastSlash(CharChunk name) { + + char[] c = name.getBuffer(); + int end = name.getEnd(); + int start = name.getStart(); + int pos = end; + + while (pos > start) { + if (c[--pos] == '/') { + break; + } + } + + return (pos); + + } + + + /** + * Find the position of the nth slash, in the given char chunk. + */ + public static final int nthSlash(CharChunk name, int n) { + + char[] c = name.getBuffer(); + int end = name.getEnd(); + int start = name.getStart(); + int pos = start; + int count = 0; + + while (pos < end) { + if ((c[pos++] == '/') && ((++count) == n)) { + pos--; + break; + } + } + + return (pos); + + } + + + /** + * Return the slash count in a given string. + */ + public static final int slashCount(String name) { + int pos = -1; + int count = 0; + while ((pos = name.indexOf('/', pos + 1)) != -1) { + count++; + } + return count; + } + + + /** + * Insert into the right place in a sorted MapElement array, and prevent + * duplicates. + */ + public static final boolean insertMap + (MapElement[] oldMap, MapElement[] newMap, MapElement newElement) { + int pos = find(oldMap, newElement.name); + if ((pos != -1) && (newElement.name.equals(oldMap[pos].name))) { + return false; + } + System.arraycopy(oldMap, 0, newMap, 0, pos + 1); + newMap[pos + 1] = newElement; + System.arraycopy + (oldMap, pos + 1, newMap, pos + 2, oldMap.length - pos - 1); + return true; + } + + + /** + * Insert into the right place in a sorted MapElement array. + */ + public static final boolean removeMap + (MapElement[] oldMap, MapElement[] newMap, String name) { + int pos = find(oldMap, name); + if ((pos != -1) && (name.equals(oldMap[pos].name))) { + System.arraycopy(oldMap, 0, newMap, 0, pos); + System.arraycopy(oldMap, pos + 1, newMap, pos, + oldMap.length - pos - 1); + return true; + } + return false; + } + + + // ------------------------------------------------- MapElement Inner Class + + + protected static abstract class MapElement { + /** hostname or path + */ + public String name = null; + public Object object = null; + + public String toString() { + return "MapElement: \"" + name +"\""; + } + } + + + // ---------------------------------------------------- Context Inner Class + + + public static final class ContextMapElement + extends MapElement { + + public String[] welcomeResources = new String[0]; + public File resources = null; + public WrapperMapElement defaultWrapper = null; + public WrapperMapElement[] exactWrappers = new WrapperMapElement[0]; + public WrapperMapElement[] wildcardWrappers = new WrapperMapElement[0]; + public WrapperMapElement[] extensionWrappers = new WrapperMapElement[0]; + public int nesting = 0; + + public String toString() { + return "ContextMapElement {" + + "name: \"" + name + + "\"\nnesting: \"" + nesting + + "\"\n}"; + } + } + + + // ---------------------------------------------------- Wrapper Inner Class + + + public static class WrapperMapElement + extends MapElement { + public boolean jspWildCard = false; + } + +} Added: tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/config/EnvEntryData.java URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/config/EnvEntryData.java?rev=433260&view=auto ============================================================================== --- tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/config/EnvEntryData.java (added) +++ tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/config/EnvEntryData.java Mon Aug 21 08:20:40 2006 @@ -0,0 +1,12 @@ +/** + * + */ +package org.apache.tomcat.servlets.config; + +import java.io.Serializable; + +public class EnvEntryData implements Serializable { + public String envEntryName; + public String envEntryType; + public String envEntryValue; +} \ No newline at end of file Added: tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/config/FilterData.java URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/config/FilterData.java?rev=433260&view=auto ============================================================================== --- tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/config/FilterData.java (added) +++ tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/config/FilterData.java Mon Aug 21 08:20:40 2006 @@ -0,0 +1,13 @@ +/** + * + */ +package org.apache.tomcat.servlets.config; + +import java.io.Serializable; +import java.util.HashMap; + +public class FilterData implements Serializable { + public HashMap initParams = new HashMap(); + public String filterClass; + public String filterName; +} \ No newline at end of file Added: tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/config/FilterMappingData.java URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/config/FilterMappingData.java?rev=433260&view=auto ============================================================================== --- tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/config/FilterMappingData.java (added) +++ tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/config/FilterMappingData.java Mon Aug 21 08:20:40 2006 @@ -0,0 +1,14 @@ +/** + * + */ +package org.apache.tomcat.servlets.config; + +import java.io.Serializable; +import java.util.ArrayList; + +public class FilterMappingData implements Serializable { + public String filterName; + public String urlPattern; + public String servletName; + public ArrayList dispatcher = new ArrayList(); +} \ No newline at end of file Added: tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/config/SecurityConstraintData.java URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/config/SecurityConstraintData.java?rev=433260&view=auto ============================================================================== --- tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/config/SecurityConstraintData.java (added) +++ tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/config/SecurityConstraintData.java Mon Aug 21 08:20:40 2006 @@ -0,0 +1,15 @@ +/** + * + */ +package org.apache.tomcat.servlets.config; + +import java.io.Serializable; +import java.util.ArrayList; + +public class SecurityConstraintData implements Serializable { + public ArrayList roleName = new ArrayList(); // auth-constraint/role + + public ArrayList webResourceCollection = new ArrayList(); + public String transportGuarantee; + +} \ No newline at end of file Added: tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/config/ServletData.java URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/config/ServletData.java?rev=433260&view=auto ============================================================================== --- tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/config/ServletData.java (added) +++ tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/config/ServletData.java Mon Aug 21 08:20:40 2006 @@ -0,0 +1,25 @@ +/** + * + */ +package org.apache.tomcat.servlets.config; + +import java.io.Serializable; +import java.util.HashMap; + +public class ServletData implements Serializable { + public ServletData() { + } + public ServletData(String servletName, String servletClass) { + this.servletClass = servletClass; + this.serlvetName = servletName; + } + + public HashMap initParams = new HashMap(); + public String serlvetName; + public String servletClass; + public String jspFile; + public int loadOnStartup; + public String runAs; + public HashMap securityRoleRef = new HashMap(); // roleName -> [roleLink] + +} \ No newline at end of file Added: tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/config/WebAppData.java URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/config/WebAppData.java?rev=433260&view=auto ============================================================================== --- tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/config/WebAppData.java (added) +++ tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/config/WebAppData.java Mon Aug 21 08:20:40 2006 @@ -0,0 +1,58 @@ +/** + * + */ +package org.apache.tomcat.servlets.config; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; + +public class WebAppData implements Serializable { + + public String fileName; + public long timestamp; + + public boolean full; + public HashMap contextParam = new HashMap(); + public HashMap mimeMapping = new HashMap(); // extension -> mime-type + + public ArrayList listenerClass = new ArrayList(); + + public ArrayList welcomeFileList = new ArrayList(); + public HashMap errorPageCode= new HashMap(); // code -> location + public HashMap errorPageException= new HashMap(); // exception -> location + public HashMap localeEncodingMapping= new HashMap(); // locale -> encoding + + // public HashMap tagLibs; // uri->location + // jsp-property-group + + // securityConstraint + public ArrayList securityConstraint = new ArrayList(); + // loginConfig + public String authMethod; + public String realmName; + public String formLoginPage; + public String formErrorPage; + + // securityRole + public ArrayList securityRole = new ArrayList(); + + // envEntry + public ArrayList envEntry = new ArrayList(); + + // ejbRef + // ejbLocalRef + // serviceRef + // resourceRef + // resourceEnvRef + // message-destination + // message-destinationRef + public HashMap filters = new HashMap(); + public HashMap servlets = new HashMap(); + + public int sessionTimeout; + public boolean distributable; + + public HashMap servletMapping = new HashMap(); // url -> servlet + public ArrayList filterMappings = new ArrayList(); +} \ No newline at end of file Added: tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/config/WebResourceCollectionData.java URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/config/WebResourceCollectionData.java?rev=433260&view=auto ============================================================================== --- tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/config/WebResourceCollectionData.java (added) +++ tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/config/WebResourceCollectionData.java Mon Aug 21 08:20:40 2006 @@ -0,0 +1,13 @@ +/** + * + */ +package org.apache.tomcat.servlets.config; + +import java.io.Serializable; +import java.util.ArrayList; + +public class WebResourceCollectionData implements Serializable { + public String webResourceName; + public ArrayList urlPattern = new ArrayList(); + public ArrayList httpMethod = new ArrayList(); +} \ No newline at end of file Added: tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/deploy/InitServlet.java URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/deploy/InitServlet.java?rev=433260&view=auto ============================================================================== --- tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/deploy/InitServlet.java (added) +++ tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/deploy/InitServlet.java Mon Aug 21 08:20:40 2006 @@ -0,0 +1,77 @@ +package org.apache.tomcat.servlets.deploy; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.tomcat.lite.TomcatLite; +import org.apache.tomcat.lite.TomcatLite.ContextConfigData; +import org.apache.tomcat.lite.TomcatLite.EngineData; + +public class InitServlet extends HttpServlet { + + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException + { + EngineData ed = initConfig(); + req.setAttribute("engineData", ed); + } + + /** Read a config file or directory layouts, generate the EngineData config + * info object. + * + * The config object should include at least the set of webapps, including + * the connector webapp. Each webapp may include web.xml data, if not - + * the WebXml servlet will be called to fill it in the first time or when + * changed. + * + * It is possible to deploy tomcat-lite without a deploy webapp - in which + * case the EngineData will be used as reference, and now web.xml will be + * refreshed. + * + * Alternative implementations: + * - various layouts and configs + * - static set of settings ( hardcoded ) + * + * @return + */ + public EngineData initConfig() { + EngineData ed = new TomcatLite.EngineData(); + + File webappD = new File("webapps"); + if (!webappD.exists()) { + return ed; + } + File[] dirs = webappD.listFiles(); + for (File dir: dirs) { + if (dir.isDirectory()) { + String path = dir.getName(); + // TODO: demangling, etc + if ("ROOT".equals(path)) { + path = "/"; + } else { + path = "/" + path; + } + ed.contexts.add(new ContextConfigData(dir.getAbsolutePath(), + path)); + } + } + + return ed; + } + + public static void main(String args[]) throws IOException { + InitServlet is = new InitServlet(); + EngineData ed = is.initConfig(); + TomcatLite.getServletImpl().saveEngineData(ed); + } + +} Added: tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/deploy/ReloadServlet.java URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/deploy/ReloadServlet.java?rev=433260&view=auto ============================================================================== --- tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/deploy/ReloadServlet.java (added) +++ tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/deploy/ReloadServlet.java Mon Aug 21 08:20:40 2006 @@ -0,0 +1,23 @@ +package org.apache.tomcat.servlets.deploy; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.tomcat.lite.TomcatLite; + +public class ReloadServlet extends HttpServlet { + + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + + TomcatLite servletImpl = TomcatLite.getServletImpl(); + //servletImpl.reloadServletContext(getServletContext()); + + } + + +} Added: tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/deploy/WebAnnotation.java URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/deploy/WebAnnotation.java?rev=433260&view=auto ============================================================================== --- tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/deploy/WebAnnotation.java (added) +++ tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/deploy/WebAnnotation.java Mon Aug 21 08:20:40 2006 @@ -0,0 +1,145 @@ +package org.apache.tomcat.servlets.deploy; + +import java.util.List; + +import javax.annotation.security.DeclareRoles; +import javax.annotation.security.RunAs; + +import org.apache.tomcat.servlets.config.FilterData; +import org.apache.tomcat.servlets.config.ServletData; +import org.apache.tomcat.servlets.config.WebAppData; + + + +/** + * Based on catalina.WebAnnotationSet + * + * Supports: + * @DeclaresRoles - on Servlet class - web-app/security-role/role-name + * @RunAs - on Servlet class - web-app/servlet/run-as + * + * + * No support for jndi @Resources, @Resource + * No @InjectionComplete callback annotation + * + * No support for @EJB, @WebServiceRef + * + * @author costin + * @author Fabien Carrion + */ +public class WebAnnotation { + + /** + * Process the annotations on a context. + */ + public static void loadApplicationAnnotations(WebAppData context, ClassLoader classLoader) { + loadApplicationListenerAnnotations(context, classLoader); + loadApplicationFilterAnnotations(context, classLoader); + loadApplicationServletAnnotations(context, classLoader); + } + + + // -------------------------------------------------------- protected Methods + + + /** + * Process the annotations for the listeners. + */ + static void loadApplicationListenerAnnotations(WebAppData context, ClassLoader classLoader) { + List applicationListeners = context.listenerClass; + for (int i = 0; i < applicationListeners.size(); i++) { + loadClassAnnotation(context, (String)applicationListeners.get(i), classLoader); + } + } + + + /** + * Process the annotations for the filters. + */ + static void loadApplicationFilterAnnotations(WebAppData context, ClassLoader classLoader) { + for(FilterData fc: context.filters.values()) { + loadClassAnnotation(context, fc.filterClass, classLoader); + } + } + + + /** + * Process the annotations for the servlets. + * @param classLoader + */ + static void loadApplicationServletAnnotations(WebAppData context, ClassLoader classLoader) { + Class classClass = null; + + for (ServletData sd: context.servlets.values()) { + if (sd.servletClass == null) { + continue; + } + + try { + classClass = classLoader.loadClass(sd.servletClass); + } catch (ClassNotFoundException e) { + // We do nothing + } catch (NoClassDefFoundError e) { + // We do nothing + } + + if (classClass == null) { + continue; + } + + loadClassAnnotation(context, classClass); + /* Process RunAs annotation which can be only on servlets. + * Ref JSR 250, equivalent to the run-as element in + * the deployment descriptor + */ + if (classClass.isAnnotationPresent(RunAs.class)) { + RunAs annotation = (RunAs) + classClass.getAnnotation(RunAs.class); + sd.runAs = annotation.value(); + } + } + } + + /** + * Process the annotations on a context for a given className. + */ + static void loadClassAnnotation(WebAppData context, + String classClass2, ClassLoader classLoader) { + + Class classClass = null; + + try { + classClass = classLoader.loadClass(classClass2); + } catch (ClassNotFoundException e) { + // We do nothing + } catch (NoClassDefFoundError e) { + // We do nothing + } + + if (classClass == null) { + return; + } + loadClassAnnotation(context, classClass); + } + + static void loadClassAnnotation(WebAppData context, + Class classClass) { + + /* Process DeclareRoles annotation. + * Ref JSR 250, equivalent to the security-role element in + * the deployment descriptor + */ + if (classClass.isAnnotationPresent(DeclareRoles.class)) { + DeclareRoles annotation = (DeclareRoles) + classClass.getAnnotation(DeclareRoles.class); + for (int i = 0; annotation.value() != null && + i < annotation.value().length; i++) { + context.securityRole.add(annotation.value()[i]); + } + } + + + } + + +} Added: tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/deploy/WebXml.java URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/deploy/WebXml.java?rev=433260&view=auto ============================================================================== --- tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/deploy/WebXml.java (added) +++ tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/deploy/WebXml.java Mon Aug 21 08:20:40 2006 @@ -0,0 +1,347 @@ +/* + */ +package org.apache.tomcat.servlets.deploy; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.HashMap; + +import javax.servlet.ServletException; + +import org.apache.tomcat.servlets.config.EnvEntryData; +import org.apache.tomcat.servlets.config.FilterData; +import org.apache.tomcat.servlets.config.FilterMappingData; +import org.apache.tomcat.servlets.config.SecurityConstraintData; +import org.apache.tomcat.servlets.config.ServletData; +import org.apache.tomcat.servlets.config.WebAppData; +import org.apache.tomcat.servlets.config.WebResourceCollectionData; +import org.apache.tomcat.util.DomUtil; +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +/** Process an web.xml file + * + * @author costin + */ +public class WebXml { + WebAppData d = new WebAppData(); + + public WebXml() { + } + + public void saveWebAppData(String fileName) throws IOException { + ObjectOutputStream oos = + new ObjectOutputStream(new FileOutputStream(fileName)); + oos.writeObject(d); + oos.close(); + } + + public WebAppData getWebAppData() { + return d; + } + + public void readWebXml(String baseDir) throws ServletException { + try { + File webXmlFile = new File( baseDir + "/WEB-INF/web.xml"); + if (!webXmlFile.exists()) { + return; + } + d.fileName = webXmlFile.getCanonicalPath(); + d.timestamp = webXmlFile.lastModified(); + + FileInputStream fileInputStream = new FileInputStream(webXmlFile); + Document document = DomUtil.readXml(fileInputStream); + Node webappNode = DomUtil.getChild(document, "web-app"); + + String fullS = DomUtil.getAttribute(webappNode, "full"); + if (fullS != null && fullS.equalsIgnoreCase("true")) { + d.full = true; + } + // Process each child of web-app + Node confNode = DomUtil.getChild(webappNode, "filter"); + while (confNode != null ) { + processFilter(confNode); + confNode = DomUtil.getNext(confNode); + } + + confNode = DomUtil.getChild(webappNode, "filter-mapping"); + while (confNode != null ) { + processFilterMapping(confNode); + confNode = DomUtil.getNext(confNode); + } + + confNode = DomUtil.getChild(webappNode, "context-param"); + while (confNode != null ) { + String n = DomUtil.getChildContent(confNode, "param-name").trim(); + String v = DomUtil.getChildContent(confNode, "param-value").trim(); + d.contextParam.put(n, v); + confNode = DomUtil.getNext(confNode); + } + + confNode = DomUtil.getChild(webappNode, "mime-mapping"); + while (confNode != null ) { + String n = DomUtil.getChildContent(confNode, "extension"); + String t = DomUtil.getChildContent(confNode, "mime-type"); + d.mimeMapping.put(n, t); + confNode = DomUtil.getNext(confNode); + } + + confNode = DomUtil.getChild(webappNode, "error-page"); + while (confNode != null ) { + processErrorPage(confNode); + confNode = DomUtil.getNext(confNode); + } + + confNode = DomUtil.getChild(webappNode, "jsp-config"); + while (confNode != null ) { + processJspConfig(confNode); + confNode = DomUtil.getNext(confNode); + } + + confNode = DomUtil.getChild(webappNode, "servlet"); + while (confNode != null ) { + processServlet(confNode); + confNode = DomUtil.getNext(confNode); + } + + confNode = DomUtil.getChild(webappNode, "servlet-mapping"); + while (confNode != null ) { + processServletMapping(confNode); + confNode = DomUtil.getNext(confNode); + } + + confNode = DomUtil.getChild(webappNode, "listener"); + while (confNode != null ) { + String lClass = DomUtil.getChildContent(confNode, "listener-class"); + d.listenerClass.add(lClass); + confNode = DomUtil.getNext(confNode); + } + + confNode = DomUtil.getChild(webappNode, "security-constraint"); + while (confNode != null ) { + processSecurityConstraint(confNode); + confNode = DomUtil.getNext(confNode); + } + + confNode = DomUtil.getChild(webappNode, "login-config"); + while (confNode != null ) { + processLoginConfig(confNode); + confNode = DomUtil.getNext(confNode); + if (confNode != null) + throw new ServletException("Multiple login-config"); + } + + confNode = DomUtil.getChild(webappNode, "session-config"); + while (confNode != null ) { + confNode = DomUtil.getNext(confNode); + if (confNode != null) + throw new ServletException("Multiple session-config"); + String n = DomUtil.getChildContent(confNode, "session-timeout"); + int stout = Integer.parseInt(n); + d.sessionTimeout = stout; + } + + confNode = DomUtil.getChild(webappNode, "welcome-file-list"); + while (confNode != null ) { + Node wf = DomUtil.getChild(confNode, "welcome-file"); + while (wf != null) { + String file = DomUtil.getContent(wf); + d.welcomeFileList.add(file); + wf = DomUtil.getNext(wf); + } + // more sections ? + confNode = DomUtil.getNext(confNode); + } + + // Not supported right now - TODO: collect, have jndi plugin + confNode = DomUtil.getChild(webappNode, "env-entry"); + while (confNode != null ) { + processEnvEntry(confNode); + confNode = DomUtil.getNext(confNode); + } + + confNode = DomUtil.getChild(webappNode, "locale-encoding-mapping-list"); + while (confNode != null ) { + confNode = DomUtil.getNext(confNode); + String n = DomUtil.getChildContent(confNode, "locale"); + String t = DomUtil.getChildContent(confNode, "encoding"); + d.localeEncodingMapping.put(n, t); + } + + confNode = DomUtil.getChild(webappNode, "distributable"); + while (confNode != null ) { + d.distributable = true; + confNode = DomUtil.getNext(confNode); + } + + confNode = DomUtil.getChild(confNode, "security-role"); + while (confNode != null ) { + String n = DomUtil.getChildContent(confNode, "role-name"); + d.securityRole.add(n); + confNode = DomUtil.getNext(confNode); + } + + } catch (Exception e) { + e.printStackTrace(); + throw new ServletException(e); + } + } + + private void processJspConfig(Node confNode) { + Node tagLib = DomUtil.getChild(confNode, "taglib"); + while (tagLib != null) { + String uri = DomUtil.getChildContent(tagLib, "taglib-uri"); + String l = DomUtil.getChildContent(tagLib, "taglib-location"); + //d.tagLibs.put(uri, l); + tagLib = DomUtil.getNext(tagLib); + } + + tagLib = DomUtil.getChild(confNode, "jsp-property-group"); + while (tagLib != null) { + // That would be the job of the JSP servlet to process. + tagLib = DomUtil.getNext(tagLib); + } + } + + private void processEnvEntry(Node confNode) { + EnvEntryData ed = new EnvEntryData(); + ed.envEntryName = DomUtil.getChildContent(confNode,"env-entry-name"); + ed.envEntryType = DomUtil.getChildContent(confNode,"env-entry-type"); + ed.envEntryValue = DomUtil.getChildContent(confNode,"env-entry-value"); + d.envEntry.add(ed); + } + + private void processLoginConfig(Node confNode) { + d.authMethod = DomUtil.getChildContent(confNode,"auth-method"); + d.realmName = DomUtil.getChildContent(confNode,"auth-method"); + Node formNode = DomUtil.getChild(confNode, "form-login-config"); + if (formNode != null) { + d.formLoginPage = DomUtil.getChildContent(formNode,"form-login-page"); + d.formErrorPage = DomUtil.getChildContent(formNode,"form-error-page"); + } + } + + private void processSecurityConstraint(Node confNode) { + SecurityConstraintData sd = new SecurityConstraintData(); + Node cn = DomUtil.getChild(confNode, "web-resource-collection"); + while (cn != null) { + WebResourceCollectionData wrd = new WebResourceCollectionData(); + wrd.webResourceName = DomUtil.getChildContent(cn, "web-resource-name"); + Node scn = DomUtil.getChild(cn,"url-pattern"); + while (scn != null) { + wrd.urlPattern.add(DomUtil.getContent(scn)); + scn = DomUtil.getNext(scn); + } + scn = DomUtil.getChild(cn,"http-method"); + while (scn != null) { + wrd.httpMethod.add(DomUtil.getContent(scn)); + scn = DomUtil.getNext(scn); + } + cn = DomUtil.getNext(cn); + } + d.securityConstraint.add(sd); + } + + private void processErrorPage(Node confNode) { + String name = DomUtil.getChildContent(confNode,"error-location"); + String c = DomUtil.getChildContent(confNode,"error-code"); + String t = DomUtil.getChildContent(confNode,"exception-type"); + if (c != null) { + d.errorPageCode.put(c, name); + } + if (t != null) { + d.errorPageException.put(t, name); + } + } + + private void processServlet(Node confNode) throws ServletException { + ServletData sd = new ServletData(); + + sd.serlvetName = DomUtil.getChildContent(confNode,"servlet-name"); + sd.servletClass = DomUtil.getChildContent(confNode,"servlet-class"); + sd.jspFile = DomUtil.getChildContent(confNode,"jsp-file"); + + processInitParams(confNode, sd.initParams); + + d.servlets.put( sd.serlvetName, sd ); + + String los = DomUtil.getChildContent(confNode, "load-on-startup"); + if (los != null ) { + sd.loadOnStartup = Integer.parseInt(los); + } + + Node sn = DomUtil.getChild(confNode, "security-role-ref"); + while (sn != null ) { + String roleName = DomUtil.getChildContent(sn, "role-name"); + String roleLink = DomUtil.getChildContent(sn, "role-link"); + if (roleLink == null) { + sd.securityRoleRef.put(roleName, ""); + } else { + sd.securityRoleRef.put(roleName, roleLink); + } + sn = DomUtil.getNext(sn); + } + } + + private void processInitParams(Node confNode, HashMap initParams) { + Node initN = DomUtil.getChild(confNode, "init-param"); + while (initN != null ) { + String n = DomUtil.getChildContent(initN, "param-name"); + String v = DomUtil.getChildContent(initN, "param-value"); + initParams.put(n, v); + initN = DomUtil.getNext(initN); + } + } + + private void processServletMapping(Node confNode) { + String name = DomUtil.getChildContent(confNode,"servlet-name"); + String path = DomUtil.getChildContent(confNode,"url-pattern"); + + d.servletMapping.put(path, name); + } + + private void processFilterMapping(Node confNode) { + FilterMappingData fm = new FilterMappingData(); + fm.filterName = DomUtil.getChildContent(confNode,"filter-name"); + // multiple + ArrayList dispatchers = new ArrayList(); + Node dataN = DomUtil.getChild(confNode, "dispatcher"); + while (dataN != null ) { + String d = DomUtil.getContent(dataN); + dispatchers.add(d); + dataN = DomUtil.getNext(dataN); + } + fm.dispatcher = dispatchers; + + dataN = DomUtil.getChild(confNode, "url-pattern"); + while (dataN != null ) { + String path = DomUtil.getContent(dataN); + dataN = DomUtil.getNext(dataN); + fm.urlPattern = path; + } + dataN = DomUtil.getChild(confNode, "servlet-name"); + while (dataN != null ) { + String sn = DomUtil.getContent(dataN); + dataN = DomUtil.getNext(dataN); + fm.servletName = sn; + } + d.filterMappings.add(fm); + } + + private void processFilter(Node confNode) { + String name = DomUtil.getChildContent(confNode,"filter-name"); + String sclass = DomUtil.getChildContent(confNode,"filter-class"); + + FilterData fd = new FilterData(); + processInitParams(confNode, fd.initParams); + fd.filterName = name; + fd.filterClass = sclass; + d.filters.put(name, fd); + } + +} Added: tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/session/HttpSessionImpl.java URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/session/HttpSessionImpl.java?rev=433260&view=auto ============================================================================== --- tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/session/HttpSessionImpl.java (added) +++ tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/servlets/session/HttpSessionImpl.java Mon Aug 21 08:20:40 2006 @@ -0,0 +1,784 @@ +/* + * Copyright 1999,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package org.apache.tomcat.servlets.session; + + +import java.io.Serializable; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpSession; +import javax.servlet.http.HttpSessionAttributeListener; +import javax.servlet.http.HttpSessionBindingEvent; +import javax.servlet.http.HttpSessionBindingListener; +import javax.servlet.http.HttpSessionContext; +import javax.servlet.http.HttpSessionEvent; +import javax.servlet.http.HttpSessionListener; + +import org.apache.tomcat.servlets.util.Enumerator; + +/** + * Standard implementation of the Session interface. + * + * This is a minimal, non-serializable HttpSession. You can use a different + * session manager, but you should keep in mind that persistent or distributed + * sessions are a bad thing. + * + * The session is best for caching data across requests, and tracking the + * user flow. For any data you don't want to lose or is worth preserving - + * use a transaction manager, or any form of storage that provides the + * set of ACID characteristics you need. + * + * Even the most sophisticated sessions managers can't guarantee data integrity + * in 100% of cases, and can't notify you of the cases where a replication + * failed. Using such a manager might fool users into making incorrect + * assumptions. Computers and networks do crash at random points, and all + * the theory on transactions exists for a good reason. + * + * Note: this is a user-space implementation, i.e. this can be used in any + * container by using the WebappSessionManager class. + * + * @author Costin Manolache - removed most of the code + * @author Craig R. McClanahan + * @author Sean Legassick + * @author Jon S. Stevens + */ +public class HttpSessionImpl implements HttpSession, Serializable { + + /** + * Type array, used as param to toArray() + */ + protected static final String EMPTY_ARRAY[] = new String[0]; + + /** + * The HTTP session context associated with this session. + */ + protected static HttpSessionContext sessionContext = null; + + + /** + * The Manager with which this Session is associated. + */ + protected transient SessionManagerServlet manager = null; + + /** + * The session identifier of this Session. + */ + protected String id = null; + + /** + * The collection of user data attributes associated with this Session. + */ + protected Map attributes = new HashMap(); + + /** + * The time this session was created, in milliseconds since midnight, + * January 1, 1970 GMT. + */ + protected long creationTime = 0L; + + /** + * The last accessed time for this Session. + */ + protected long lastAccessedTime = creationTime; + + /** + * The current accessed time for this session. + */ + protected long thisAccessedTime = creationTime; + + /** + * The maximum time interval, in seconds, between client requests before + * the servlet container may invalidate this session. A negative time + * indicates that the session should never time out. + */ + protected int maxInactiveInterval = -1; + + /** + * The access count for this session - how many requests are using this + * session ( so we can prevent expiry ) + */ + protected transient int accessCount = 0; + + /** + * We are currently processing a session expiration, so bypass + * certain IllegalStateException tests. + */ + protected transient boolean expiring = false; + + + /** + * Flag indicating whether this session is new or not. + */ + protected boolean isNew = false; + + + /** + * Flag indicating whether this session is valid or not. + */ + protected boolean isValid = false; + + + /** Only the manager can create sessions, so it knows about them. + */ + HttpSessionImpl(SessionManagerServlet manager) { + this.manager = manager; + } + + // ---------- API methods --------- + + /** + * Return the session identifier for this session. + */ + public String getId() { + return this.id; + } + + /** + * Return the last time the client sent a request associated with this + * session, as the number of milliseconds since midnight, January 1, 1970 + * GMT. Actions that your application takes, such as getting or setting + * a value associated with the session, do not affect the access time. + */ + public long getLastAccessedTime() { + checkValid(); + return this.lastAccessedTime; + + } + + /** + * Return the maximum time interval, in seconds, between client requests + * before the servlet container will invalidate the session. A negative + * time indicates that the session should never time out. + */ + public int getMaxInactiveInterval() { + return this.maxInactiveInterval; + } + + + /** + * Set the maximum time interval, in seconds, between client requests + * before the servlet container will invalidate the session. A negative + * time indicates that the session should never time out. + * + * @param interval The new maximum interval + */ + public void setMaxInactiveInterval(int interval) { + this.maxInactiveInterval = interval; + if (isValid && interval == 0) { + expire(); + } + } + + /** + * Return the time when this session was created, in milliseconds since + * midnight, January 1, 1970 GMT. + * + * @exception IllegalStateException if this method is called on an + * invalidated session + */ + public long getCreationTime() { + checkValid(); + return this.creationTime; + + } + + public ServletContext getServletContext() { + if (manager == null) + return null; // Should never happen + ServletContext context = (ServletContext)manager.getContext(); + return context; + } + + + public HttpSessionContext getSessionContext() { + if (sessionContext == null) + sessionContext = new StandardSessionContext(); + return (sessionContext); + } + + /** + * Return the object bound with the specified name in this session, or + * null if no object is bound with that name. + * + * @param name Name of the attribute to be returned + * + * @exception IllegalStateException if this method is called on an + * invalidated session + */ + public Object getAttribute(String name) { + checkValid(); + return attributes.get(name); + } + + + /** + * Return an Enumeration of String objects + * containing the names of the objects bound to this session. + * + * @exception IllegalStateException if this method is called on an + * invalidated session + */ + public Enumeration getAttributeNames() { + checkValid(); + return new Enumerator(attributes.keySet(), true); + } + + + /** + * Return the object bound with the specified name in this session, or + * null if no object is bound with that name. + * + * @param name Name of the value to be returned + * + * @exception IllegalStateException if this method is called on an + * invalidated session + * + * @deprecated As of Version 2.2, this method is replaced by + * getAttribute() + */ + public Object getValue(String name) { + return (getAttribute(name)); + } + + + /** + * Return the set of names of objects bound to this session. If there + * are no such objects, a zero-length array is returned. + * + * @exception IllegalStateException if this method is called on an + * invalidated session + * + * @deprecated As of Version 2.2, this method is replaced by + * getAttributeNames() + */ + public String[] getValueNames() { + checkValid(); + return keys(); // same, but no check for validity + } + + + /** + * Invalidates this session and unbinds any objects bound to it. + * + * @exception IllegalStateException if this method is called on + * an invalidated session + */ + public void invalidate() { + checkValid(); + expire(); + } + + + /** + * Return true if the client does not yet know about the + * session, or if the client chooses not to join the session. For + * example, if the server used only cookie-based sessions, and the client + * has disabled the use of cookies, then a session would be new on each + * request. + * + * @exception IllegalStateException if this method is called on an + * invalidated session + */ + public boolean isNew() { + checkValid(); + return (this.isNew); + } + + public void putValue(String name, Object value) { + setAttribute(name, value); + } + + public void removeAttribute(String name) { + checkValid(); + removeAttributeInternal(name, true); + } + + public void removeValue(String name) { + removeAttribute(name); + } + + + /** + * Bind an object to this session, using the specified name. If an object + * of the same name is already bound to this session, the object is + * replaced. + *

+ * After this method executes, and if the object implements + * HttpSessionBindingListener, the container calls + * valueBound() on the object. + * + * @param name Name to which the object is bound, cannot be null + * @param value Object to be bound, cannot be null + * + * @exception IllegalArgumentException if an attempt is made to add a + * non-serializable object in an environment marked distributable. + * @exception IllegalStateException if this method is called on an + * invalidated session + */ + public void setAttribute(String name, Object value) { + // Name cannot be null + if (name == null) return; + + // Null value is the same as removeAttribute() + if (value == null) { + removeAttribute(name); + return; + } + + checkValid(); + + if ((manager != null) && manager.getDistributable() && + !(value instanceof Serializable)) + throw new IllegalArgumentException("setAttribute() not serializable"); + + // Construct an event with the new value + HttpSessionBindingEvent event = null; + + // Call the valueBound() method if necessary + if (value instanceof HttpSessionBindingListener) { + // Don't call any notification if replacing with the same value + Object oldValue = attributes.get(name); + if (value != oldValue) { + event = new HttpSessionBindingEvent(getSession(), name, value); + try { + ((HttpSessionBindingListener) value).valueBound(event); + } catch (Throwable t){ + manager.log.error("Listener valueBound() error", t); + } + } + } + + // Replace or add this attribute + Object unbound = attributes.put(name, value); + + // Call the valueUnbound() method if necessary + if ((unbound != null) && (unbound != value) && + (unbound instanceof HttpSessionBindingListener)) { + try { + ((HttpSessionBindingListener) unbound).valueUnbound + (new HttpSessionBindingEvent(getSession(), name)); + } catch (Throwable t) { + manager.log.error("Listener valueUnbound()", t); + } + } + + // Notify interested application event listeners + ServletContext context = manager.getContext(); + List listeners = manager.getEventListeners(); + if (listeners.size() == 0) + return; + for (int i = 0; i < listeners.size(); i++) { + if (!(listeners.get(i) instanceof HttpSessionAttributeListener)) + continue; + HttpSessionAttributeListener listener = + (HttpSessionAttributeListener) listeners.get(i); + try { + if (unbound != null) { + if (event == null) { + event = new HttpSessionBindingEvent + (getSession(), name, unbound); + } + listener.attributeReplaced(event); + } else { + if (event == null) { + event = new HttpSessionBindingEvent + (getSession(), name, value); + } + listener.attributeAdded(event); + } + } catch (Throwable t) { + manager.log.error("Listener attibuteAdded/Replaced()", t); + } + } + + } + + // -------- Implementation - interactions with SessionManager ----- + + /** + * Set the creation time for this session. This method is called by the + * Manager when an existing Session instance is reused. + */ + public void setCreationTime(long time) { + this.creationTime = time; + this.lastAccessedTime = time; + this.thisAccessedTime = time; + } + + /** + * Set the session identifier for this session and notify listeners about + * new session + * + * @param id The new session identifier + */ + public void setId(String id) { + + if ((this.id != null) && (manager != null)) + manager.remove(this); + + this.id = id; + + if (manager != null) + manager.add(this); + tellNew(); + } + + + /** + * Inform the listeners about the new session. + */ + public void tellNew() { + // Notify interested application event listeners + ServletContext context = manager.getContext(); + List listeners = manager.getEventListeners(); + if (listeners.size() > 0) { + HttpSessionEvent event = + new HttpSessionEvent(getSession()); + for (int i = 0; i < listeners.size(); i++) { + Object listenerObj = listeners.get(i); + if (!(listenerObj instanceof HttpSessionListener)) + continue; + HttpSessionListener listener = + (HttpSessionListener) listenerObj; + try { + listener.sessionCreated(event); + } catch (Throwable t) { + manager.log.error("listener.sessionCreated()", t); + } + } + } + + } + + /** + * Return the Manager within which this Session is valid. + */ + public SessionManagerServlet getManager() { + return (this.manager); + } + + + + /** + * Set the isNew flag for this session. + * + * @param isNew The new value for the isNew flag + */ + public void setNew(boolean isNew) { + this.isNew = isNew; + } + + public HttpSession getSession() { + return this; + } + + private void checkValid() { + if ( !isValid() ) { + throw new IllegalStateException("checkValid"); + } + } + + /** + * Return the isValid flag for this session. + */ + public boolean isValid() { + if (this.expiring) { + return true; + } + if (!this.isValid ) { + return false; + } + if (accessCount > 0) { + return true; + } + if (maxInactiveInterval >= 0) { + long timeNow = System.currentTimeMillis(); + int timeIdle = (int) ((timeNow - thisAccessedTime) / 1000L); + if (timeIdle >= maxInactiveInterval) { + expire(true); + } + } + return this.isValid; + } + + public void setValid(boolean isValid) { + this.isValid = isValid; + } + + /** + * Update the accessed time information for this session. This method + * should be called by the context when a request comes in for a particular + * session, even if the application does not reference it. + */ + public void access() { + this.lastAccessedTime = this.thisAccessedTime; + this.thisAccessedTime = System.currentTimeMillis(); + + evaluateIfValid(); + accessCount++; + } + + + /** + * End the access. + */ + public void endAccess() { + isNew = false; + accessCount--; + } + + /** + * Perform the internal processing required to invalidate this session, + * without triggering an exception if the session has already expired. + */ + public void expire() { + expire(true); + } + + + /** + * Perform the internal processing required to invalidate this session, + * without triggering an exception if the session has already expired. + * + * @param notify Should we notify listeners about the demise of + * this session? + */ + public void expire(boolean notify) { + + // Mark this session as "being expired" if needed + if (expiring) + return; + + synchronized (this) { + + if (manager == null) + return; + + expiring = true; + + // Notify interested application event listeners + // FIXME - Assumes we call listeners in reverse order + ServletContext context = manager.getContext(); + List listeners = manager.getEventListeners(); + if (notify && (listeners.size() > 0)) { + HttpSessionEvent event = + new HttpSessionEvent(getSession()); + for (int i = 0; i < listeners.size(); i++) { + Object listenerObj = listeners.get(i); + int j = (listeners.size() - 1) - i; + if (!(listenerObj instanceof HttpSessionListener)) + continue; + HttpSessionListener listener = + (HttpSessionListener) listenerObj; + try { + listener.sessionDestroyed(event); + } catch (Throwable t) { + manager.log.error("listener.sessionDestroyed", t); + } + } + } + accessCount = 0; + isValid = false; + + /* + * Compute how long this session has been alive, and update + * session manager's related properties accordingly + */ + long timeNow = System.currentTimeMillis(); + int timeAlive = (int) ((timeNow - creationTime)/1000); + manager.addExpiredSession(timeAlive); + + // Remove this session from our manager's active sessions + manager.remove(this); + + expiring = false; + + // Unbind any objects associated with this session + String keys[] = keys(); + for (int i = 0; i < keys.length; i++) + removeAttributeInternal(keys[i], notify); + } + } + + + /** + * Release all object references, and initialize instance variables, in + * preparation for reuse of this object. + */ + public void recycle() { + + // Reset the instance variables associated with this Session + attributes.clear(); + creationTime = 0L; + expiring = false; + id = null; + lastAccessedTime = 0L; + maxInactiveInterval = -1; + accessCount = 0; + isNew = false; + isValid = false; + manager = null; + + } + + /** + * Return a string representation of this object. + */ + public String toString() { + + StringBuffer sb = new StringBuffer(); + sb.append("StandardSession["); + sb.append(id); + sb.append("]"); + return (sb.toString()); + } + + protected void evaluateIfValid() { + /* + * If this session has expired or is in the process of expiring or + * will never expire, return + */ + if (!this.isValid || expiring || maxInactiveInterval < 0) + return; + + isValid(); + + } + + /** + * Return the names of all currently defined session attributes + * as an array of Strings. If there are no defined attributes, a + * zero-length array is returned. + */ + protected String[] keys() { + return ((String[]) attributes.keySet().toArray(EMPTY_ARRAY)); + } + + + /** + * Remove the object bound with the specified name from this session. If + * the session does not have an object bound with this name, this method + * does nothing. + *

+ * After this method executes, and if the object implements + * HttpSessionBindingListener, the container calls + * valueUnbound() on the object. + * + * @param name Name of the object to remove from this session. + * @param notify Should we notify interested listeners that this + * attribute is being removed? + */ + protected void removeAttributeInternal(String name, boolean notify) { + // Remove this attribute from our collection + Object value = attributes.remove(name); + + // Do we need to do valueUnbound() and attributeRemoved() notification? + if (!notify || (value == null)) { + return; + } + + // Call the valueUnbound() method if necessary + HttpSessionBindingEvent event = null; + if (value instanceof HttpSessionBindingListener) { + event = new HttpSessionBindingEvent(getSession(), name, value); + ((HttpSessionBindingListener) value).valueUnbound(event); + } + + // Notify interested application event listeners + ServletContext context = manager.getContext(); + List listeners = manager.getEventListeners(); + if (listeners.size() == 0) + return; + for (int i = 0; i < listeners.size(); i++) { + if (!(listeners.get(i) instanceof HttpSessionAttributeListener)) + continue; + HttpSessionAttributeListener listener = + (HttpSessionAttributeListener) listeners.get(i); + try { + if (event == null) { + event = new HttpSessionBindingEvent + (getSession(), name, value); + } + listener.attributeRemoved(event); + } catch (Throwable t) { + manager.log.error("listener.attributeRemoved", t); + } + } + + } + +} + + +// ------------------------------------------------------------ Protected Class + + +/** + * This class is a dummy implementation of the HttpSessionContext + * interface, to conform to the requirement that such an object be returned + * when HttpSession.getSessionContext() is called. + * + * @author Craig R. McClanahan + * + * @deprecated As of Java Servlet API 2.1 with no replacement. The + * interface will be removed in a future version of this API. + */ + +final class StandardSessionContext implements HttpSessionContext { + + + protected HashMap dummy = new HashMap(); + + /** + * Return the session identifiers of all sessions defined + * within this context. + * + * @deprecated As of Java Servlet API 2.1 with no replacement. + * This method must return an empty Enumeration + * and will be removed in a future version of the API. + */ + public Enumeration getIds() { + + return (new Enumerator(dummy)); + + } + + + /** + * Return the HttpSession associated with the + * specified session identifier. + * + * @param id Session identifier for which to look up a session + * + * @deprecated As of Java Servlet API 2.1 with no replacement. + * This method must return null and will be removed in a + * future version of the API. + */ + public HttpSession getSession(String id) { + + return (null); + + } + + + +} --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org For additional commands, e-mail: dev-help@tomcat.apache.org