0001: /*
0002: * Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
0003: * Distributed under the terms of either:
0004: * - the common development and distribution license (CDDL), v1.0; or
0005: * - the GNU Lesser General Public License, v2.1 or later
0006: */
0007: package winstone;
0008:
0009: import java.io.File;
0010: import java.io.IOException;
0011: import java.io.InputStream;
0012: import java.io.PrintWriter;
0013: import java.io.StringWriter;
0014: import java.lang.reflect.Constructor;
0015: import java.net.MalformedURLException;
0016: import java.net.URL;
0017: import java.net.URLClassLoader;
0018: import java.util.ArrayList;
0019: import java.util.Arrays;
0020: import java.util.Collection;
0021: import java.util.Collections;
0022: import java.util.Comparator;
0023: import java.util.Enumeration;
0024: import java.util.HashMap;
0025: import java.util.HashSet;
0026: import java.util.Hashtable;
0027: import java.util.Iterator;
0028: import java.util.List;
0029: import java.util.Map;
0030: import java.util.Set;
0031: import java.util.StringTokenizer;
0032:
0033: import javax.servlet.ServletContext;
0034: import javax.servlet.ServletContextAttributeEvent;
0035: import javax.servlet.ServletContextAttributeListener;
0036: import javax.servlet.ServletContextEvent;
0037: import javax.servlet.ServletContextListener;
0038: import javax.servlet.ServletException;
0039: import javax.servlet.ServletRequestAttributeListener;
0040: import javax.servlet.ServletRequestListener;
0041: import javax.servlet.http.HttpServletResponse;
0042: import javax.servlet.http.HttpSessionActivationListener;
0043: import javax.servlet.http.HttpSessionAttributeListener;
0044: import javax.servlet.http.HttpSessionListener;
0045:
0046: import org.w3c.dom.Node;
0047: import org.w3c.dom.NodeList;
0048:
0049: /**
0050: * Models the web.xml file's details ... basically just a bunch of configuration
0051: * details, plus the actual instances of mounted servlets.
0052: *
0053: * @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
0054: * @version $Id: WebAppConfiguration.java,v 1.55 2007/11/13 01:42:47 rickknowles Exp $
0055: */
0056: public class WebAppConfiguration implements ServletContext, Comparator {
0057: // private static final String ELEM_DESCRIPTION = "description";
0058: private static final String ELEM_DISPLAY_NAME = "display-name";
0059: private static final String ELEM_SERVLET = "servlet";
0060: private static final String ELEM_SERVLET_MAPPING = "servlet-mapping";
0061: private static final String ELEM_SERVLET_NAME = "servlet-name";
0062: private static final String ELEM_FILTER = "filter";
0063: private static final String ELEM_FILTER_MAPPING = "filter-mapping";
0064: private static final String ELEM_FILTER_NAME = "filter-name";
0065: private static final String ELEM_DISPATCHER = "dispatcher";
0066: private static final String ELEM_URL_PATTERN = "url-pattern";
0067: private static final String ELEM_WELCOME_FILES = "welcome-file-list";
0068: private static final String ELEM_WELCOME_FILE = "welcome-file";
0069: private static final String ELEM_SESSION_CONFIG = "session-config";
0070: private static final String ELEM_SESSION_TIMEOUT = "session-timeout";
0071: private static final String ELEM_MIME_MAPPING = "mime-mapping";
0072: private static final String ELEM_MIME_EXTENSION = "extension";
0073: private static final String ELEM_MIME_TYPE = "mime-type";
0074: private static final String ELEM_CONTEXT_PARAM = "context-param";
0075: private static final String ELEM_PARAM_NAME = "param-name";
0076: private static final String ELEM_PARAM_VALUE = "param-value";
0077: private static final String ELEM_LISTENER = "listener";
0078: private static final String ELEM_LISTENER_CLASS = "listener-class";
0079: private static final String ELEM_DISTRIBUTABLE = "distributable";
0080: private static final String ELEM_ERROR_PAGE = "error-page";
0081: private static final String ELEM_EXCEPTION_TYPE = "exception-type";
0082: private static final String ELEM_ERROR_CODE = "error-code";
0083: private static final String ELEM_ERROR_LOCATION = "location";
0084: private static final String ELEM_SECURITY_CONSTRAINT = "security-constraint";
0085: private static final String ELEM_LOGIN_CONFIG = "login-config";
0086: private static final String ELEM_SECURITY_ROLE = "security-role";
0087: private static final String ELEM_ROLE_NAME = "role-name";
0088: private static final String ELEM_ENV_ENTRY = "env-entry";
0089: private static final String ELEM_LOCALE_ENC_MAP_LIST = "locale-encoding-mapping-list";
0090: private static final String ELEM_LOCALE_ENC_MAPPING = "locale-encoding-mapping";
0091: private static final String ELEM_LOCALE = "locale";
0092: private static final String ELEM_ENCODING = "encoding";
0093: private static final String ELEM_JSP_CONFIG = "jsp-config";
0094: private static final String ELEM_JSP_PROPERTY_GROUP = "jsp-property-group";
0095:
0096: private static final String DISPATCHER_REQUEST = "REQUEST";
0097: private static final String DISPATCHER_FORWARD = "FORWARD";
0098: private static final String DISPATCHER_INCLUDE = "INCLUDE";
0099: private static final String DISPATCHER_ERROR = "ERROR";
0100: private static final String JSP_SERVLET_NAME = "JspServlet";
0101: private static final String JSP_SERVLET_MAPPING = "*.jsp";
0102: private static final String JSPX_SERVLET_MAPPING = "*.jspx";
0103: private static final String JSP_SERVLET_LOG_LEVEL = "WARNING";
0104: private static final String INVOKER_SERVLET_NAME = "invoker";
0105: private static final String INVOKER_SERVLET_CLASS = "winstone.invoker.InvokerServlet";
0106: private static final String DEFAULT_INVOKER_PREFIX = "/servlet/";
0107: private static final String DEFAULT_SERVLET_NAME = "default";
0108: private static final String DEFAULT_SERVLET_CLASS = "winstone.StaticResourceServlet";
0109: private static final String DEFAULT_REALM_CLASS = "winstone.realm.ArgumentsRealm";
0110: private static final String DEFAULT_JNDI_MGR_CLASS = "winstone.jndi.WebAppJNDIManager";
0111: private static final String RELOADING_CL_CLASS = "winstone.classLoader.ReloadingClassLoader";
0112: private static final String WEBAPP_CL_CLASS = "winstone.classLoader.WebappClassLoader";
0113: private static final String ERROR_SERVLET_NAME = "winstoneErrorServlet";
0114: private static final String ERROR_SERVLET_CLASS = "winstone.ErrorServlet";
0115:
0116: private static final String WEB_INF = "WEB-INF";
0117: private static final String CLASSES = "classes/";
0118: private static final String LIB = "lib";
0119:
0120: static final String JSP_SERVLET_CLASS = "org.apache.jasper.servlet.JspServlet";
0121:
0122: private HostConfiguration ownerHostConfig;
0123: private Cluster cluster;
0124: private String webRoot;
0125: private String prefix;
0126: private String contextName;
0127: private ClassLoader loader;
0128: private String displayName;
0129: private Map attributes;
0130: private Map initParameters;
0131: private Map sessions;
0132: private Map mimeTypes;
0133: private Map servletInstances;
0134: private Map filterInstances;
0135: private ServletContextAttributeListener contextAttributeListeners[];
0136: private ServletContextListener contextListeners[];
0137: private ServletRequestListener requestListeners[];
0138: private ServletRequestAttributeListener requestAttributeListeners[];
0139: private HttpSessionActivationListener sessionActivationListeners[];
0140: private HttpSessionAttributeListener sessionAttributeListeners[];
0141: private HttpSessionListener sessionListeners[];
0142: private Throwable contextStartupError;
0143: private Map exactServletMatchMounts;
0144: private Mapping patternMatches[];
0145: private Mapping filterPatternsRequest[];
0146: private Mapping filterPatternsForward[];
0147: private Mapping filterPatternsInclude[];
0148: private Mapping filterPatternsError[];
0149: private AuthenticationHandler authenticationHandler;
0150: private AuthenticationRealm authenticationRealm;
0151: private String welcomeFiles[];
0152: private Integer sessionTimeout;
0153: private Class[] errorPagesByExceptionKeysSorted;
0154: private Map errorPagesByException;
0155: private Map errorPagesByCode;
0156: private Map localeEncodingMap;
0157: private String defaultServletName;
0158: private String errorServletName;
0159: private JNDIManager jndiManager;
0160: private AccessLogger accessLogger;
0161: private Map filterMatchCache;
0162: private boolean useSavedSessions;
0163:
0164: public static boolean booleanArg(Map args, String name,
0165: boolean defaultTrue) {
0166: String value = (String) args.get(name);
0167: if (defaultTrue)
0168: return (value == null)
0169: || (value.equalsIgnoreCase("true") || value
0170: .equalsIgnoreCase("yes"));
0171: else
0172: return (value != null)
0173: && (value.equalsIgnoreCase("true") || value
0174: .equalsIgnoreCase("yes"));
0175: }
0176:
0177: public static String stringArg(Map args, String name,
0178: String defaultValue) {
0179: return (String) (args.get(name) == null ? defaultValue : args
0180: .get(name));
0181: }
0182:
0183: public static int intArg(Map args, String name, int defaultValue) {
0184: return Integer
0185: .parseInt(stringArg(args, name, "" + defaultValue));
0186: }
0187:
0188: public static String getTextFromNode(Node node) {
0189: if (node == null) {
0190: return null;
0191: }
0192: Node child = node.getFirstChild();
0193: if (child == null) {
0194: return "";
0195: }
0196: String textNode = child.getNodeValue();
0197: if (textNode == null) {
0198: return "";
0199: } else {
0200: return textNode.trim();
0201: }
0202: }
0203:
0204: public static boolean useSavedSessions(Map args) {
0205: return booleanArg(args, "useSavedSessions", false);
0206: }
0207:
0208: /**
0209: * Constructor. This parses the xml and sets up for basic routing
0210: */
0211: public WebAppConfiguration(HostConfiguration ownerHostConfig,
0212: Cluster cluster, String webRoot, String prefix,
0213: ObjectPool objectPool, Map startupArgs, Node elm,
0214: ClassLoader parentClassLoader, File parentClassPaths[],
0215: String contextName) {
0216: if (!prefix.equals("") && !prefix.startsWith("/")) {
0217: Logger.log(Logger.WARNING, Launcher.RESOURCES,
0218: "WebAppConfig.AddingLeadingSlash", prefix);
0219: prefix = "/" + prefix;
0220: }
0221: this .ownerHostConfig = ownerHostConfig;
0222: this .webRoot = webRoot;
0223: this .prefix = prefix;
0224: this .contextName = contextName;
0225:
0226: List localLoaderClassPathFiles = new ArrayList();
0227: this .loader = buildWebAppClassLoader(startupArgs,
0228: parentClassLoader, webRoot, localLoaderClassPathFiles);
0229:
0230: // Build switch values
0231: boolean useJasper = booleanArg(startupArgs, "useJasper", true);
0232: boolean useInvoker = booleanArg(startupArgs, "useInvoker",
0233: false);
0234: boolean useJNDI = booleanArg(startupArgs, "useJNDI", false);
0235: this .useSavedSessions = useSavedSessions(startupArgs);
0236:
0237: // Check jasper is available - simple tests
0238: if (useJasper) {
0239: try {
0240: Class.forName("javax.servlet.jsp.JspFactory", true,
0241: parentClassLoader);
0242: Class.forName(JSP_SERVLET_CLASS, true, this .loader);
0243: } catch (Throwable err) {
0244: if (booleanArg(startupArgs, "useJasper", false)) {
0245: Logger.log(Logger.WARNING, Launcher.RESOURCES,
0246: "WebAppConfig.JasperNotFound");
0247: Logger.log(Logger.DEBUG, Launcher.RESOURCES,
0248: "WebAppConfig.JasperLoadException", err);
0249: }
0250: useJasper = false;
0251: }
0252: }
0253: if (useInvoker) {
0254: try {
0255: Class
0256: .forName(INVOKER_SERVLET_CLASS, false,
0257: this .loader);
0258: } catch (Throwable err) {
0259: Logger.log(Logger.WARNING, Launcher.RESOURCES,
0260: "WebAppConfig.InvokerNotFound");
0261: useInvoker = false;
0262: }
0263: }
0264:
0265: this .attributes = new Hashtable();
0266: this .initParameters = new HashMap();
0267: this .sessions = new Hashtable();
0268:
0269: this .servletInstances = new HashMap();
0270: this .filterInstances = new HashMap();
0271: this .filterMatchCache = new HashMap();
0272:
0273: List contextAttributeListeners = new ArrayList();
0274: List contextListeners = new ArrayList();
0275: List requestListeners = new ArrayList();
0276: List requestAttributeListeners = new ArrayList();
0277: List sessionActivationListeners = new ArrayList();
0278: List sessionAttributeListeners = new ArrayList();
0279: List sessionListeners = new ArrayList();
0280:
0281: this .errorPagesByException = new HashMap();
0282: this .errorPagesByCode = new HashMap();
0283: boolean distributable = false;
0284:
0285: this .exactServletMatchMounts = new Hashtable();
0286: List localFolderPatterns = new ArrayList();
0287: List localExtensionPatterns = new ArrayList();
0288:
0289: List lfpRequest = new ArrayList();
0290: List lfpForward = new ArrayList();
0291: List lfpInclude = new ArrayList();
0292: List lfpError = new ArrayList();
0293:
0294: List localWelcomeFiles = new ArrayList();
0295: List startupServlets = new ArrayList();
0296:
0297: Set rolesAllowed = new HashSet();
0298: List constraintNodes = new ArrayList();
0299: List envEntryNodes = new ArrayList();
0300: List localErrorPagesByExceptionList = new ArrayList();
0301:
0302: Node loginConfigNode = null;
0303:
0304: // Add the class loader as an implicit context listener if it implements the interface
0305: addListenerInstance(this .loader, contextAttributeListeners,
0306: contextListeners, requestAttributeListeners,
0307: requestListeners, sessionActivationListeners,
0308: sessionAttributeListeners, sessionListeners);
0309:
0310: // init mimeTypes set
0311: this .mimeTypes = new Hashtable();
0312: String allTypes = Launcher.RESOURCES
0313: .getString("WebAppConfig.DefaultMimeTypes");
0314: StringTokenizer mappingST = new StringTokenizer(allTypes, ":",
0315: false);
0316: for (; mappingST.hasMoreTokens();) {
0317: String mapping = mappingST.nextToken();
0318: int delimPos = mapping.indexOf('=');
0319: if (delimPos == -1)
0320: continue;
0321: String extension = mapping.substring(0, delimPos);
0322: String mimeType = mapping.substring(delimPos + 1);
0323: this .mimeTypes.put(extension.toLowerCase(), mimeType);
0324: }
0325:
0326: this .localeEncodingMap = new HashMap();
0327: String encodingMapSet = Launcher.RESOURCES
0328: .getString("WebAppConfig.EncodingMap");
0329: StringTokenizer st = new StringTokenizer(encodingMapSet, ";");
0330: for (; st.hasMoreTokens();) {
0331: String token = st.nextToken();
0332: int delimPos = token.indexOf("=");
0333: if (delimPos == -1)
0334: continue;
0335: this .localeEncodingMap.put(token.substring(0, delimPos),
0336: token.substring(delimPos + 1));
0337: }
0338:
0339: // init jsp mappings set
0340: List jspMappings = new ArrayList();
0341: jspMappings.add(JSP_SERVLET_MAPPING);
0342: jspMappings.add(JSPX_SERVLET_MAPPING);
0343:
0344: // Add required context atttributes
0345: File tmpDir = new File(new File(new File(System
0346: .getProperty("java.io.tmpdir"), "winstone.tmp"),
0347: ownerHostConfig.getHostname()), contextName);
0348: tmpDir.mkdirs();
0349: this .attributes.put("javax.servlet.context.tempdir", tmpDir);
0350:
0351: // Parse the web.xml file
0352: if (elm != null) {
0353: NodeList children = elm.getChildNodes();
0354: for (int n = 0; n < children.getLength(); n++) {
0355: Node child = children.item(n);
0356: if (child.getNodeType() != Node.ELEMENT_NODE)
0357: continue;
0358: String nodeName = child.getNodeName();
0359:
0360: if (nodeName.equals(ELEM_DISPLAY_NAME))
0361: this .displayName = getTextFromNode(child);
0362:
0363: else if (nodeName.equals(ELEM_DISTRIBUTABLE))
0364: distributable = true;
0365:
0366: else if (nodeName.equals(ELEM_SECURITY_CONSTRAINT))
0367: constraintNodes.add(child);
0368:
0369: else if (nodeName.equals(ELEM_ENV_ENTRY))
0370: envEntryNodes.add(child);
0371:
0372: else if (nodeName.equals(ELEM_LOGIN_CONFIG))
0373: loginConfigNode = child;
0374:
0375: // Session config elements
0376: else if (nodeName.equals(ELEM_SESSION_CONFIG)) {
0377: for (int m = 0; m < child.getChildNodes()
0378: .getLength(); m++) {
0379: Node timeoutElm = child.getChildNodes().item(m);
0380: if ((timeoutElm.getNodeType() == Node.ELEMENT_NODE)
0381: && (timeoutElm.getNodeName()
0382: .equals(ELEM_SESSION_TIMEOUT))) {
0383: String timeoutStr = getTextFromNode(timeoutElm);
0384: if (!timeoutStr.equals("")) {
0385: this .sessionTimeout = Integer
0386: .valueOf(timeoutStr);
0387: }
0388: }
0389: }
0390: }
0391:
0392: // Construct the security roles
0393: else if (child.getNodeName().equals(ELEM_SECURITY_ROLE)) {
0394: for (int m = 0; m < child.getChildNodes()
0395: .getLength(); m++) {
0396: Node roleElm = child.getChildNodes().item(m);
0397: if ((roleElm.getNodeType() == Node.ELEMENT_NODE)
0398: && (roleElm.getNodeName()
0399: .equals(ELEM_ROLE_NAME)))
0400: rolesAllowed.add(getTextFromNode(roleElm));
0401: }
0402: }
0403:
0404: // Construct the servlet instances
0405: else if (nodeName.equals(ELEM_SERVLET)) {
0406: ServletConfiguration instance = new ServletConfiguration(
0407: this , child);
0408: this .servletInstances.put(
0409: instance.getServletName(), instance);
0410: if (instance.getLoadOnStartup() >= 0)
0411: startupServlets.add(instance);
0412: }
0413:
0414: // Construct the servlet instances
0415: else if (nodeName.equals(ELEM_FILTER)) {
0416: FilterConfiguration instance = new FilterConfiguration(
0417: this , this .loader, child);
0418: this .filterInstances.put(instance.getFilterName(),
0419: instance);
0420: }
0421:
0422: // Construct the servlet instances
0423: else if (nodeName.equals(ELEM_LISTENER)) {
0424: String listenerClass = null;
0425: for (int m = 0; m < child.getChildNodes()
0426: .getLength(); m++) {
0427: Node listenerElm = child.getChildNodes()
0428: .item(m);
0429: if ((listenerElm.getNodeType() == Node.ELEMENT_NODE)
0430: && (listenerElm.getNodeName()
0431: .equals(ELEM_LISTENER_CLASS)))
0432: listenerClass = getTextFromNode(listenerElm);
0433: }
0434: if (listenerClass != null)
0435: try {
0436: Class listener = Class.forName(
0437: listenerClass, true, this .loader);
0438: Object listenerInstance = listener
0439: .newInstance();
0440: addListenerInstance(listenerInstance,
0441: contextAttributeListeners,
0442: contextListeners,
0443: requestAttributeListeners,
0444: requestListeners,
0445: sessionActivationListeners,
0446: sessionAttributeListeners,
0447: sessionListeners);
0448: Logger.log(Logger.DEBUG,
0449: Launcher.RESOURCES,
0450: "WebAppConfig.AddListener",
0451: listenerClass);
0452: } catch (Throwable err) {
0453: Logger.log(Logger.WARNING,
0454: Launcher.RESOURCES,
0455: "WebAppConfig.InvalidListener",
0456: listenerClass);
0457: }
0458: }
0459:
0460: // Process the servlet mappings
0461: else if (nodeName.equals(ELEM_SERVLET_MAPPING)) {
0462: String name = null;
0463: List mappings = new ArrayList();
0464:
0465: // Parse the element and extract
0466: NodeList mappingChildren = child.getChildNodes();
0467: for (int k = 0; k < mappingChildren.getLength(); k++) {
0468: Node mapChild = mappingChildren.item(k);
0469: if (mapChild.getNodeType() != Node.ELEMENT_NODE)
0470: continue;
0471: String mapNodeName = mapChild.getNodeName();
0472: if (mapNodeName.equals(ELEM_SERVLET_NAME)) {
0473: name = getTextFromNode(mapChild);
0474: } else if (mapNodeName.equals(ELEM_URL_PATTERN)) {
0475: mappings.add(getTextFromNode(mapChild));
0476: }
0477: }
0478: for (Iterator i = mappings.iterator(); i.hasNext();) {
0479: processMapping(name, (String) i.next(),
0480: this .exactServletMatchMounts,
0481: localFolderPatterns,
0482: localExtensionPatterns);
0483: }
0484: }
0485:
0486: // Process the filter mappings
0487: else if (nodeName.equals(ELEM_FILTER_MAPPING)) {
0488: String filterName = null;
0489: List mappings = new ArrayList();
0490: boolean onRequest = false;
0491: boolean onForward = false;
0492: boolean onInclude = false;
0493: boolean onError = false;
0494:
0495: // Parse the element and extract
0496: for (int k = 0; k < child.getChildNodes()
0497: .getLength(); k++) {
0498: Node mapChild = child.getChildNodes().item(k);
0499: if (mapChild.getNodeType() != Node.ELEMENT_NODE)
0500: continue;
0501: String mapNodeName = mapChild.getNodeName();
0502: if (mapNodeName.equals(ELEM_FILTER_NAME)) {
0503: filterName = getTextFromNode(mapChild);
0504: } else if (mapNodeName
0505: .equals(ELEM_SERVLET_NAME)) {
0506: mappings.add("srv:"
0507: + getTextFromNode(mapChild));
0508: } else if (mapNodeName.equals(ELEM_URL_PATTERN)) {
0509: mappings.add("url:"
0510: + getTextFromNode(mapChild));
0511: } else if (mapNodeName.equals(ELEM_DISPATCHER)) {
0512: String dispatcherValue = getTextFromNode(mapChild);
0513: if (dispatcherValue
0514: .equals(DISPATCHER_REQUEST))
0515: onRequest = true;
0516: else if (dispatcherValue
0517: .equals(DISPATCHER_FORWARD))
0518: onForward = true;
0519: else if (dispatcherValue
0520: .equals(DISPATCHER_INCLUDE))
0521: onInclude = true;
0522: else if (dispatcherValue
0523: .equals(DISPATCHER_ERROR))
0524: onError = true;
0525: }
0526: }
0527: if (!onRequest && !onInclude && !onForward
0528: && !onError) {
0529: onRequest = true;
0530: }
0531: if (mappings.isEmpty()) {
0532: throw new WinstoneException(
0533: Launcher.RESOURCES
0534: .getString(
0535: "WebAppConfig.BadFilterMapping",
0536: filterName));
0537: }
0538:
0539: for (Iterator i = mappings.iterator(); i.hasNext();) {
0540: String item = (String) i.next();
0541: Mapping mapping = null;
0542: try {
0543: if (item.startsWith("srv:")) {
0544: mapping = Mapping.createFromLink(
0545: filterName, item.substring(4));
0546: } else {
0547: mapping = Mapping.createFromURL(
0548: filterName, item.substring(4));
0549: }
0550: if (onRequest)
0551: lfpRequest.add(mapping);
0552: if (onForward)
0553: lfpForward.add(mapping);
0554: if (onInclude)
0555: lfpInclude.add(mapping);
0556: if (onError)
0557: lfpError.add(mapping);
0558: } catch (WinstoneException err) {
0559: Logger.log(Logger.WARNING,
0560: Launcher.RESOURCES,
0561: "WebAppConfig.ErrorMapURL", err
0562: .getMessage());
0563: }
0564: }
0565: }
0566:
0567: // Process the list of welcome files
0568: else if (nodeName.equals(ELEM_WELCOME_FILES)) {
0569: for (int m = 0; m < child.getChildNodes()
0570: .getLength(); m++) {
0571: Node welcomeFile = child.getChildNodes()
0572: .item(m);
0573: if ((welcomeFile.getNodeType() == Node.ELEMENT_NODE)
0574: && welcomeFile.getNodeName().equals(
0575: ELEM_WELCOME_FILE)) {
0576: String welcomeStr = getTextFromNode(welcomeFile);
0577: if (!welcomeStr.equals("")) {
0578: localWelcomeFiles.add(welcomeStr);
0579: }
0580: }
0581: }
0582: }
0583:
0584: // Process the error pages
0585: else if (nodeName.equals(ELEM_ERROR_PAGE)) {
0586: String code = null;
0587: String exception = null;
0588: String location = null;
0589:
0590: // Parse the element and extract
0591: for (int k = 0; k < child.getChildNodes()
0592: .getLength(); k++) {
0593: Node errorChild = child.getChildNodes().item(k);
0594: if (errorChild.getNodeType() != Node.ELEMENT_NODE)
0595: continue;
0596: String errorChildName = errorChild
0597: .getNodeName();
0598: if (errorChildName.equals(ELEM_ERROR_CODE))
0599: code = getTextFromNode(errorChild);
0600: else if (errorChildName
0601: .equals(ELEM_EXCEPTION_TYPE))
0602: exception = getTextFromNode(errorChild);
0603: else if (errorChildName
0604: .equals(ELEM_ERROR_LOCATION))
0605: location = getTextFromNode(errorChild);
0606: }
0607: if ((code != null) && (location != null))
0608: this .errorPagesByCode.put(code.trim(), location
0609: .trim());
0610: if ((exception != null) && (location != null))
0611: try {
0612: Class exceptionClass = Class.forName(
0613: exception.trim(), false,
0614: this .loader);
0615: localErrorPagesByExceptionList
0616: .add(exceptionClass);
0617: this .errorPagesByException.put(
0618: exceptionClass, location.trim());
0619: } catch (ClassNotFoundException err) {
0620: Logger.log(Logger.ERROR,
0621: Launcher.RESOURCES,
0622: "WebAppConfig.ExceptionNotFound",
0623: exception);
0624: }
0625: }
0626:
0627: // Process the list of welcome files
0628: else if (nodeName.equals(ELEM_MIME_MAPPING)) {
0629: String extension = null;
0630: String mimeType = null;
0631: for (int m = 0; m < child.getChildNodes()
0632: .getLength(); m++) {
0633: Node mimeTypeNode = child.getChildNodes().item(
0634: m);
0635: if (mimeTypeNode.getNodeType() != Node.ELEMENT_NODE)
0636: continue;
0637: else if (mimeTypeNode.getNodeName().equals(
0638: ELEM_MIME_EXTENSION))
0639: extension = getTextFromNode(mimeTypeNode);
0640: else if (mimeTypeNode.getNodeName().equals(
0641: ELEM_MIME_TYPE))
0642: mimeType = getTextFromNode(mimeTypeNode);
0643: }
0644: if ((extension != null) && (mimeType != null))
0645: this .mimeTypes.put(extension.toLowerCase(),
0646: mimeType);
0647: else
0648: Logger.log(Logger.WARNING, Launcher.RESOURCES,
0649: "WebAppConfig.InvalidMimeMapping",
0650: new String[] { extension, mimeType });
0651: }
0652:
0653: // Process the list of welcome files
0654: else if (nodeName.equals(ELEM_CONTEXT_PARAM)) {
0655: String name = null;
0656: String value = null;
0657: for (int m = 0; m < child.getChildNodes()
0658: .getLength(); m++) {
0659: Node contextParamNode = child.getChildNodes()
0660: .item(m);
0661: if (contextParamNode.getNodeType() != Node.ELEMENT_NODE)
0662: continue;
0663: else if (contextParamNode.getNodeName().equals(
0664: ELEM_PARAM_NAME))
0665: name = getTextFromNode(contextParamNode);
0666: else if (contextParamNode.getNodeName().equals(
0667: ELEM_PARAM_VALUE))
0668: value = getTextFromNode(contextParamNode);
0669: }
0670: if ((name != null) && (value != null))
0671: this .initParameters.put(name, value);
0672: else
0673: Logger.log(Logger.WARNING, Launcher.RESOURCES,
0674: "WebAppConfig.InvalidInitParam",
0675: new String[] { name, value });
0676: }
0677:
0678: // Process locale encoding mapping elements
0679: else if (nodeName.equals(ELEM_LOCALE_ENC_MAP_LIST)) {
0680: for (int m = 0; m < child.getChildNodes()
0681: .getLength(); m++) {
0682: Node mappingNode = child.getChildNodes()
0683: .item(m);
0684: if (mappingNode.getNodeType() != Node.ELEMENT_NODE)
0685: continue;
0686: else if (mappingNode.getNodeName().equals(
0687: ELEM_LOCALE_ENC_MAPPING)) {
0688: String localeName = "";
0689: String encoding = "";
0690: for (int l = 0; l < mappingNode
0691: .getChildNodes().getLength(); l++) {
0692: Node mappingChildNode = mappingNode
0693: .getChildNodes().item(l);
0694: if (mappingChildNode.getNodeType() != Node.ELEMENT_NODE)
0695: continue;
0696: else if (mappingChildNode.getNodeName()
0697: .equals(ELEM_LOCALE))
0698: localeName = getTextFromNode(mappingChildNode);
0699: else if (mappingChildNode.getNodeName()
0700: .equals(ELEM_ENCODING))
0701: encoding = getTextFromNode(mappingChildNode);
0702: }
0703: if (!encoding.equals("")
0704: && !localeName.equals(""))
0705: this .localeEncodingMap.put(localeName,
0706: encoding);
0707: }
0708: }
0709: }
0710:
0711: // Record the url mappings for jsp files if set
0712: else if (nodeName.equals(ELEM_JSP_CONFIG)) {
0713: for (int m = 0; m < child.getChildNodes()
0714: .getLength(); m++) {
0715: Node propertyGroupNode = child.getChildNodes()
0716: .item(m);
0717: if ((propertyGroupNode.getNodeType() == Node.ELEMENT_NODE)
0718: && propertyGroupNode
0719: .getNodeName()
0720: .equals(ELEM_JSP_PROPERTY_GROUP)) {
0721: for (int l = 0; l < propertyGroupNode
0722: .getChildNodes().getLength(); l++) {
0723: Node urlPatternNode = propertyGroupNode
0724: .getChildNodes().item(l);
0725: if ((urlPatternNode.getNodeType() == Node.ELEMENT_NODE)
0726: && urlPatternNode
0727: .getNodeName()
0728: .equals(
0729: ELEM_URL_PATTERN)) {
0730: String jm = getTextFromNode(urlPatternNode);
0731: if (!jm.equals("")) {
0732: jspMappings.add(jm);
0733: }
0734: }
0735: }
0736: }
0737: }
0738: }
0739: }
0740: }
0741:
0742: // If not distributable, remove the cluster reference
0743: if (!distributable && (cluster != null)) {
0744: Logger.log(Logger.INFO, Launcher.RESOURCES,
0745: "WebAppConfig.ClusterOffNotDistributable",
0746: this .contextName);
0747: } else {
0748: this .cluster = cluster;
0749: }
0750:
0751: // Build the login/security role instance
0752: if (!constraintNodes.isEmpty() && (loginConfigNode != null)) {
0753: String authMethod = null;
0754: for (int n = 0; n < loginConfigNode.getChildNodes()
0755: .getLength(); n++) {
0756: if (loginConfigNode.getChildNodes().item(n)
0757: .getNodeName().equals("auth-method")) {
0758: authMethod = getTextFromNode(loginConfigNode
0759: .getChildNodes().item(n));
0760: }
0761: }
0762: // Load the appropriate auth class
0763: if (authMethod == null) {
0764: authMethod = "BASIC";
0765: } else {
0766: authMethod = WinstoneResourceBundle.globalReplace(
0767: authMethod, "-", "");
0768: }
0769: String realmClassName = stringArg(startupArgs,
0770: "realmClassName", DEFAULT_REALM_CLASS).trim();
0771: String authClassName = "winstone.auth."
0772: + authMethod.substring(0, 1).toUpperCase()
0773: + authMethod.substring(1).toLowerCase()
0774: + "AuthenticationHandler";
0775: try {
0776: // Build the realm
0777: Class realmClass = Class.forName(realmClassName, true,
0778: parentClassLoader);
0779: Constructor realmConstr = realmClass
0780: .getConstructor(new Class[] { Set.class,
0781: Map.class });
0782: this .authenticationRealm = (AuthenticationRealm) realmConstr
0783: .newInstance(new Object[] { rolesAllowed,
0784: startupArgs });
0785:
0786: // Build the authentication handler
0787: Class authClass = Class.forName(authClassName);
0788: Constructor authConstr = authClass
0789: .getConstructor(new Class[] { Node.class,
0790: List.class, Set.class,
0791: AuthenticationRealm.class });
0792: this .authenticationHandler = (AuthenticationHandler) authConstr
0793: .newInstance(new Object[] { loginConfigNode,
0794: constraintNodes, rolesAllowed,
0795: authenticationRealm });
0796: } catch (ClassNotFoundException err) {
0797: Logger.log(Logger.DEBUG, Launcher.RESOURCES,
0798: "WebAppConfig.AuthDisabled", authMethod);
0799: } catch (Throwable err) {
0800: Logger.log(Logger.ERROR, Launcher.RESOURCES,
0801: "WebAppConfig.AuthError", new String[] {
0802: authClassName, realmClassName }, err);
0803: }
0804: } else if (!stringArg(startupArgs, "realmClassName", "").trim()
0805: .equals("")) {
0806: Logger.log(Logger.DEBUG, Launcher.RESOURCES,
0807: "WebAppConfig.NoWebXMLSecurityDefs");
0808: }
0809:
0810: // Instantiate the JNDI manager
0811: String jndiMgrClassName = stringArg(startupArgs,
0812: "webappJndiClassName", DEFAULT_JNDI_MGR_CLASS).trim();
0813: if (useJNDI) {
0814: try {
0815: // Build the realm
0816: Class jndiMgrClass = Class.forName(jndiMgrClassName,
0817: true, parentClassLoader);
0818: Constructor jndiMgrConstr = jndiMgrClass
0819: .getConstructor(new Class[] { Map.class,
0820: List.class, ClassLoader.class });
0821: this .jndiManager = (JNDIManager) jndiMgrConstr
0822: .newInstance(new Object[] { null,
0823: envEntryNodes, this .loader });
0824: if (this .jndiManager != null)
0825: this .jndiManager.setup();
0826: } catch (ClassNotFoundException err) {
0827: Logger.log(Logger.DEBUG, Launcher.RESOURCES,
0828: "WebAppConfig.JNDIDisabled");
0829: } catch (Throwable err) {
0830: Logger
0831: .log(Logger.ERROR, Launcher.RESOURCES,
0832: "WebAppConfig.JNDIError",
0833: jndiMgrClassName, err);
0834: }
0835: }
0836:
0837: String loggerClassName = stringArg(startupArgs,
0838: "accessLoggerClassName", "").trim();
0839: if (!loggerClassName.equals("")) {
0840: try {
0841: // Build the realm
0842: Class loggerClass = Class.forName(loggerClassName,
0843: true, parentClassLoader);
0844: Constructor loggerConstr = loggerClass
0845: .getConstructor(new Class[] {
0846: WebAppConfiguration.class, Map.class });
0847: this .accessLogger = (AccessLogger) loggerConstr
0848: .newInstance(new Object[] { this , startupArgs });
0849: } catch (Throwable err) {
0850: Logger.log(Logger.ERROR, Launcher.RESOURCES,
0851: "WebAppConfig.LoggerError", loggerClassName,
0852: err);
0853: }
0854: } else {
0855: Logger.log(Logger.DEBUG, Launcher.RESOURCES,
0856: "WebAppConfig.LoggerDisabled");
0857:
0858: }
0859:
0860: // Add the default index.html welcomeFile if none are supplied
0861: if (localWelcomeFiles.isEmpty()) {
0862: if (useJasper) {
0863: localWelcomeFiles.add("index.jsp");
0864: }
0865: localWelcomeFiles.add("index.html");
0866: }
0867:
0868: // Put the name filters after the url filters, then convert to string arrays
0869: this .filterPatternsRequest = (Mapping[]) lfpRequest
0870: .toArray(new Mapping[0]);
0871: this .filterPatternsForward = (Mapping[]) lfpForward
0872: .toArray(new Mapping[0]);
0873: this .filterPatternsInclude = (Mapping[]) lfpInclude
0874: .toArray(new Mapping[0]);
0875: this .filterPatternsError = (Mapping[]) lfpError
0876: .toArray(new Mapping[0]);
0877:
0878: if (this .filterPatternsRequest.length > 0)
0879: Arrays.sort(this .filterPatternsRequest,
0880: this .filterPatternsRequest[0]);
0881: if (this .filterPatternsForward.length > 0)
0882: Arrays.sort(this .filterPatternsForward,
0883: this .filterPatternsForward[0]);
0884: if (this .filterPatternsInclude.length > 0)
0885: Arrays.sort(this .filterPatternsInclude,
0886: this .filterPatternsInclude[0]);
0887: if (this .filterPatternsError.length > 0)
0888: Arrays.sort(this .filterPatternsError,
0889: this .filterPatternsError[0]);
0890:
0891: this .welcomeFiles = (String[]) localWelcomeFiles
0892: .toArray(new String[0]);
0893: this .errorPagesByExceptionKeysSorted = (Class[]) localErrorPagesByExceptionList
0894: .toArray(new Class[0]);
0895: Arrays.sort(this .errorPagesByExceptionKeysSorted, this );
0896:
0897: // Put the listeners into their arrays
0898: this .contextAttributeListeners = (ServletContextAttributeListener[]) contextAttributeListeners
0899: .toArray(new ServletContextAttributeListener[0]);
0900: this .contextListeners = (ServletContextListener[]) contextListeners
0901: .toArray(new ServletContextListener[0]);
0902: this .requestListeners = (ServletRequestListener[]) requestListeners
0903: .toArray(new ServletRequestListener[0]);
0904: this .requestAttributeListeners = (ServletRequestAttributeListener[]) requestAttributeListeners
0905: .toArray(new ServletRequestAttributeListener[0]);
0906: this .sessionActivationListeners = (HttpSessionActivationListener[]) sessionActivationListeners
0907: .toArray(new HttpSessionActivationListener[0]);
0908: this .sessionAttributeListeners = (HttpSessionAttributeListener[]) sessionAttributeListeners
0909: .toArray(new HttpSessionAttributeListener[0]);
0910: this .sessionListeners = (HttpSessionListener[]) sessionListeners
0911: .toArray(new HttpSessionListener[0]);
0912:
0913: // If we haven't explicitly mapped the default servlet, map it here
0914: if (this .defaultServletName == null)
0915: this .defaultServletName = DEFAULT_SERVLET_NAME;
0916: if (this .errorServletName == null)
0917: this .errorServletName = ERROR_SERVLET_NAME;
0918:
0919: // If we don't have an instance of the default servlet, mount the inbuilt one
0920: if (this .servletInstances.get(this .defaultServletName) == null) {
0921: boolean useDirLists = booleanArg(startupArgs,
0922: "directoryListings", true);
0923:
0924: Map staticParams = new Hashtable();
0925: staticParams.put("webRoot", webRoot);
0926: staticParams.put("prefix", this .prefix);
0927: staticParams.put("directoryList", "" + useDirLists);
0928: ServletConfiguration defaultServlet = new ServletConfiguration(
0929: this , this .defaultServletName,
0930: DEFAULT_SERVLET_CLASS, staticParams, 0);
0931: this .servletInstances.put(this .defaultServletName,
0932: defaultServlet);
0933: startupServlets.add(defaultServlet);
0934: }
0935:
0936: // If we don't have an instance of the default servlet, mount the inbuilt one
0937: if (this .servletInstances.get(this .errorServletName) == null) {
0938: ServletConfiguration errorServlet = new ServletConfiguration(
0939: this , this .errorServletName, ERROR_SERVLET_CLASS,
0940: new HashMap(), 0);
0941: this .servletInstances.put(this .errorServletName,
0942: errorServlet);
0943: startupServlets.add(errorServlet);
0944: }
0945:
0946: // Initialise jasper servlet if requested
0947: if (useJasper) {
0948: setAttribute("org.apache.catalina.classloader", this .loader);
0949: try {
0950: StringBuffer cp = new StringBuffer();
0951: for (Iterator i = localLoaderClassPathFiles.iterator(); i
0952: .hasNext();) {
0953: cp.append(((File) i.next()).getCanonicalPath())
0954: .append(File.pathSeparatorChar);
0955: }
0956: for (int n = 0; n < parentClassPaths.length; n++) {
0957: cp.append(parentClassPaths[n].getCanonicalPath())
0958: .append(File.pathSeparatorChar);
0959: }
0960: setAttribute("org.apache.catalina.jsp_classpath", (cp
0961: .length() > 0 ? cp
0962: .substring(0, cp.length() - 1) : ""));
0963: } catch (IOException err) {
0964: Logger.log(Logger.ERROR, Launcher.RESOURCES,
0965: "WebAppConfig.ErrorSettingJSPPaths", err);
0966: }
0967:
0968: Map jspParams = new HashMap();
0969: addJspServletParams(jspParams);
0970: ServletConfiguration sc = new ServletConfiguration(this ,
0971: JSP_SERVLET_NAME, JSP_SERVLET_CLASS, jspParams, 3);
0972: this .servletInstances.put(JSP_SERVLET_NAME, sc);
0973: startupServlets.add(sc);
0974: for (Iterator mapIt = jspMappings.iterator(); mapIt
0975: .hasNext();) {
0976: processMapping(JSP_SERVLET_NAME, (String) mapIt.next(),
0977: this .exactServletMatchMounts,
0978: localFolderPatterns, localExtensionPatterns);
0979: }
0980: }
0981:
0982: // Initialise invoker servlet if requested
0983: if (useInvoker) {
0984: // Get generic options
0985: String invokerPrefix = stringArg(startupArgs,
0986: "invokerPrefix", DEFAULT_INVOKER_PREFIX);
0987: Map invokerParams = new HashMap();
0988: invokerParams.put("prefix", this .prefix);
0989: invokerParams.put("invokerPrefix", invokerPrefix);
0990: ServletConfiguration sc = new ServletConfiguration(this ,
0991: INVOKER_SERVLET_NAME, INVOKER_SERVLET_CLASS,
0992: invokerParams, 3);
0993: this .servletInstances.put(INVOKER_SERVLET_NAME, sc);
0994: processMapping(INVOKER_SERVLET_NAME, invokerPrefix
0995: + Mapping.STAR, this .exactServletMatchMounts,
0996: localFolderPatterns, localExtensionPatterns);
0997: }
0998:
0999: // Sort the folder patterns so the longest paths are first
1000: localFolderPatterns.addAll(localExtensionPatterns);
1001: this .patternMatches = (Mapping[]) localFolderPatterns
1002: .toArray(new Mapping[0]);
1003: if (this .patternMatches.length > 0)
1004: Arrays.sort(this .patternMatches, this .patternMatches[0]);
1005:
1006: // Send init notifies
1007: try {
1008: for (int n = 0; n < this .contextListeners.length; n++) {
1009: ClassLoader cl = Thread.currentThread()
1010: .getContextClassLoader();
1011: Thread.currentThread().setContextClassLoader(
1012: this .loader);
1013: this .contextListeners[n]
1014: .contextInitialized(new ServletContextEvent(
1015: this ));
1016: Thread.currentThread().setContextClassLoader(cl);
1017: }
1018: } catch (Throwable err) {
1019: Logger.log(Logger.ERROR, Launcher.RESOURCES,
1020: "WebAppConfig.ContextStartupError",
1021: this .contextName, err);
1022: this .contextStartupError = err;
1023: }
1024:
1025: if (this .contextStartupError == null) {
1026: // Load sessions if enabled
1027: if (this .useSavedSessions) {
1028: WinstoneSession.loadSessions(this );
1029: }
1030:
1031: // Initialise all the filters
1032: for (Iterator i = this .filterInstances.values().iterator(); i
1033: .hasNext();) {
1034: FilterConfiguration config = (FilterConfiguration) i
1035: .next();
1036: try {
1037: config.getFilter();
1038: } catch (ServletException err) {
1039: Logger.log(Logger.ERROR, Launcher.RESOURCES,
1040: "WebAppConfig.FilterStartupError", config
1041: .getFilterName(), err);
1042: }
1043: }
1044:
1045: // Initialise load on startup servlets
1046: Object autoStarters[] = startupServlets.toArray();
1047: Arrays.sort(autoStarters);
1048: for (int n = 0; n < autoStarters.length; n++) {
1049: ((ServletConfiguration) autoStarters[n])
1050: .ensureInitialization();
1051: }
1052: }
1053: }
1054:
1055: /**
1056: * Build the web-app classloader. This tries to load the preferred classloader first,
1057: * but if it fails, falls back to a simple URLClassLoader.
1058: */
1059: private ClassLoader buildWebAppClassLoader(Map startupArgs,
1060: ClassLoader parentClassLoader, String webRoot,
1061: List classPathFileList) {
1062: List urlList = new ArrayList();
1063:
1064: try {
1065: // Web-inf folder
1066: File webInfFolder = new File(webRoot, WEB_INF);
1067:
1068: // Classes folder
1069: File classesFolder = new File(webInfFolder, CLASSES);
1070: if (classesFolder.exists()) {
1071: Logger.log(Logger.DEBUG, Launcher.RESOURCES,
1072: "WebAppConfig.WebAppClasses");
1073: String classesFolderURL = classesFolder
1074: .getCanonicalFile().toURL().toString();
1075: urlList
1076: .add(new URL(
1077: classesFolderURL.endsWith("/") ? classesFolderURL
1078: : classesFolderURL + "/"));
1079: classPathFileList.add(classesFolder);
1080: } else {
1081: Logger.log(Logger.WARNING, Launcher.RESOURCES,
1082: "WebAppConfig.NoWebAppClasses", classesFolder
1083: .toString());
1084: }
1085:
1086: // Lib folder's jar files
1087: File libFolder = new File(webInfFolder, LIB);
1088: if (libFolder.exists()) {
1089: File jars[] = libFolder.listFiles();
1090: for (int n = 0; n < jars.length; n++) {
1091: String jarName = jars[n].getName().toLowerCase();
1092: if (jarName.endsWith(".jar")
1093: || jarName.endsWith(".zip")) {
1094: Logger.log(Logger.DEBUG, Launcher.RESOURCES,
1095: "WebAppConfig.WebAppLib", jars[n]
1096: .getName());
1097: urlList.add(jars[n].toURL());
1098: classPathFileList.add(jars[n]);
1099: }
1100: }
1101: } else {
1102: Logger.log(Logger.WARNING, Launcher.RESOURCES,
1103: "WebAppConfig.NoWebAppLib", libFolder
1104: .toString());
1105: }
1106: } catch (MalformedURLException err) {
1107: throw new WinstoneException(Launcher.RESOURCES
1108: .getString("WebAppConfig.BadURL"), err);
1109: } catch (IOException err) {
1110: throw new WinstoneException(Launcher.RESOURCES
1111: .getString("WebAppConfig.IOException"), err);
1112: }
1113:
1114: URL jarURLs[] = (URL[]) urlList
1115: .toArray(new URL[urlList.size()]);
1116:
1117: String preferredClassLoader = stringArg(startupArgs,
1118: "preferredClassLoader", WEBAPP_CL_CLASS);
1119: if (booleanArg(startupArgs, "useServletReloading", false)
1120: && stringArg(startupArgs, "preferredClassLoader", "")
1121: .equals("")) {
1122: preferredClassLoader = RELOADING_CL_CLASS;
1123: }
1124:
1125: // Try to set up the preferred class loader, and if we fail, use the normal one
1126: ClassLoader outputCL = null;
1127: if (!preferredClassLoader.equals("")) {
1128: try {
1129: Class preferredCL = Class.forName(preferredClassLoader,
1130: true, parentClassLoader);
1131: Constructor reloadConstr = preferredCL
1132: .getConstructor(new Class[] { URL[].class,
1133: ClassLoader.class });
1134: outputCL = (ClassLoader) reloadConstr
1135: .newInstance(new Object[] { jarURLs,
1136: parentClassLoader });
1137: } catch (Throwable err) {
1138: if (!stringArg(startupArgs, "preferredClassLoader", "")
1139: .equals("")
1140: || !preferredClassLoader
1141: .equals(WEBAPP_CL_CLASS)) {
1142: Logger.log(Logger.ERROR, Launcher.RESOURCES,
1143: "WebAppConfig.CLError", err);
1144: }
1145: }
1146: }
1147:
1148: if (outputCL == null) {
1149: outputCL = new URLClassLoader(jarURLs, parentClassLoader);
1150: }
1151:
1152: Logger.log(Logger.MAX, Launcher.RESOURCES,
1153: "WebAppConfig.WebInfClassLoader", outputCL.toString());
1154: return outputCL;
1155: }
1156:
1157: private void addListenerInstance(Object listenerInstance,
1158: List contextAttributeListeners, List contextListeners,
1159: List requestAttributeListeners, List requestListeners,
1160: List sessionActivationListeners,
1161: List sessionAttributeListeners, List sessionListeners) {
1162: if (listenerInstance instanceof ServletContextAttributeListener)
1163: contextAttributeListeners.add(listenerInstance);
1164: if (listenerInstance instanceof ServletContextListener)
1165: contextListeners.add(listenerInstance);
1166: if (listenerInstance instanceof ServletRequestAttributeListener)
1167: requestAttributeListeners.add(listenerInstance);
1168: if (listenerInstance instanceof ServletRequestListener)
1169: requestListeners.add(listenerInstance);
1170: if (listenerInstance instanceof HttpSessionActivationListener)
1171: sessionActivationListeners.add(listenerInstance);
1172: if (listenerInstance instanceof HttpSessionAttributeListener)
1173: sessionAttributeListeners.add(listenerInstance);
1174: if (listenerInstance instanceof HttpSessionListener)
1175: sessionListeners.add(listenerInstance);
1176: }
1177:
1178: public String getContextPath() {
1179: return this .prefix;
1180: }
1181:
1182: public String getWebroot() {
1183: return this .webRoot;
1184: }
1185:
1186: public ClassLoader getLoader() {
1187: return this .loader;
1188: }
1189:
1190: public AccessLogger getAccessLogger() {
1191: return this .accessLogger;
1192: }
1193:
1194: public Map getFilters() {
1195: return this .filterInstances;
1196: }
1197:
1198: public String getContextName() {
1199: return this .contextName;
1200: }
1201:
1202: public Class[] getErrorPageExceptions() {
1203: return this .errorPagesByExceptionKeysSorted;
1204: }
1205:
1206: public Map getErrorPagesByException() {
1207: return this .errorPagesByException;
1208: }
1209:
1210: public Map getErrorPagesByCode() {
1211: return this .errorPagesByCode;
1212: }
1213:
1214: public Map getLocaleEncodingMap() {
1215: return this .localeEncodingMap;
1216: }
1217:
1218: public String[] getWelcomeFiles() {
1219: return this .welcomeFiles;
1220: }
1221:
1222: public boolean isDistributable() {
1223: return (this .cluster != null);
1224: }
1225:
1226: public Map getFilterMatchCache() {
1227: return this .filterMatchCache;
1228: }
1229:
1230: public String getOwnerHostname() {
1231: return this .ownerHostConfig.getHostname();
1232: }
1233:
1234: public ServletRequestListener[] getRequestListeners() {
1235: return this .requestListeners;
1236: }
1237:
1238: public ServletRequestAttributeListener[] getRequestAttributeListeners() {
1239: return this .requestAttributeListeners;
1240: }
1241:
1242: public static void addJspServletParams(Map jspParams) {
1243: jspParams.put("logVerbosityLevel", JSP_SERVLET_LOG_LEVEL);
1244: jspParams.put("fork", "false");
1245: }
1246:
1247: public int compare(Object one, Object two) {
1248: if (!(one instanceof Class) || !(two instanceof Class))
1249: throw new IllegalArgumentException(
1250: "This comparator is only for sorting classes");
1251: Class classOne = (Class) one;
1252: Class classTwo = (Class) two;
1253: if (classOne.isAssignableFrom(classTwo))
1254: return 1;
1255: else if (classTwo.isAssignableFrom(classOne))
1256: return -1;
1257: else
1258: return 0;
1259: }
1260:
1261: public String getServletURIFromRequestURI(String requestURI) {
1262: if (prefix.equals("")) {
1263: return requestURI;
1264: } else if (requestURI.startsWith(prefix)) {
1265: return requestURI.substring(prefix.length());
1266: } else {
1267: throw new WinstoneException("This shouldn't happen, "
1268: + "since we aborted earlier if we didn't match");
1269: }
1270: }
1271:
1272: /**
1273: * Iterates through each of the servlets/filters and calls destroy on them
1274: */
1275: public void destroy() {
1276: synchronized (this .filterMatchCache) {
1277: this .filterMatchCache.clear();
1278: }
1279:
1280: Collection filterInstances = new ArrayList(this .filterInstances
1281: .values());
1282: for (Iterator i = filterInstances.iterator(); i.hasNext();) {
1283: try {
1284: ((FilterConfiguration) i.next()).destroy();
1285: } catch (Throwable err) {
1286: Logger.log(Logger.ERROR, Launcher.RESOURCES,
1287: "WebAppConfig.ShutdownError", err);
1288: }
1289: }
1290: this .filterInstances.clear();
1291:
1292: Collection servletInstances = new ArrayList(
1293: this .servletInstances.values());
1294: for (Iterator i = servletInstances.iterator(); i.hasNext();) {
1295: try {
1296: ((ServletConfiguration) i.next()).destroy();
1297: } catch (Throwable err) {
1298: Logger.log(Logger.ERROR, Launcher.RESOURCES,
1299: "WebAppConfig.ShutdownError", err);
1300: }
1301: }
1302: this .servletInstances.clear();
1303:
1304: // Drop all sessions
1305: Collection sessions = new ArrayList(this .sessions.values());
1306: for (Iterator i = sessions.iterator(); i.hasNext();) {
1307: WinstoneSession session = (WinstoneSession) i.next();
1308: try {
1309: if (this .useSavedSessions) {
1310: session.saveToTemp();
1311: } else {
1312: session.invalidate();
1313: }
1314: } catch (Throwable err) {
1315: Logger.log(Logger.ERROR, Launcher.RESOURCES,
1316: "WebAppConfig.ShutdownError", err);
1317: }
1318: }
1319: this .sessions.clear();
1320:
1321: // Send destroy notifies - backwards
1322: for (int n = this .contextListeners.length - 1; n >= 0; n--) {
1323: try {
1324: ClassLoader cl = Thread.currentThread()
1325: .getContextClassLoader();
1326: Thread.currentThread().setContextClassLoader(
1327: this .loader);
1328: this .contextListeners[n]
1329: .contextDestroyed(new ServletContextEvent(this ));
1330: this .contextListeners[n] = null;
1331: Thread.currentThread().setContextClassLoader(cl);
1332: } catch (Throwable err) {
1333: Logger.log(Logger.ERROR, Launcher.RESOURCES,
1334: "WebAppConfig.ShutdownError", err);
1335: }
1336: }
1337: this .contextListeners = null;
1338:
1339: // Terminate class loader reloading thread if running
1340: if (this .loader != null) {
1341: // already shutdown/handled by the servlet context listeners
1342: // try {
1343: // Method methDestroy = this.loader.getClass().getMethod("destroy", new Class[0]);
1344: // methDestroy.invoke(this.loader, new Object[0]);
1345: // } catch (Throwable err) {
1346: // Logger.log(Logger.ERROR, Launcher.RESOURCES, "WebAppConfig.ShutdownError", err);
1347: // }
1348: this .loader = null;
1349: }
1350:
1351: // Kill JNDI manager if we have one
1352: if (this .jndiManager != null) {
1353: this .jndiManager.tearDown();
1354: this .jndiManager = null;
1355: }
1356:
1357: // Kill JNDI manager if we have one
1358: if (this .accessLogger != null) {
1359: this .accessLogger.destroy();
1360: this .accessLogger = null;
1361: }
1362: }
1363:
1364: /**
1365: * Triggered by the admin thread on the reloading class loader. This will
1366: * cause a full shutdown and reinstantiation of the web app - not real
1367: * graceful, but you shouldn't have reloading turned on in high load
1368: * environments.
1369: */
1370: public void resetClassLoader() throws IOException {
1371: this .ownerHostConfig.reloadWebApp(getContextPath());
1372: }
1373:
1374: /**
1375: * Here we process url patterns into the exactMatch and patternMatch lists
1376: */
1377: private void processMapping(String name, String pattern,
1378: Map exactPatterns, List folderPatterns,
1379: List extensionPatterns) {
1380:
1381: Mapping urlPattern = null;
1382: try {
1383: urlPattern = Mapping.createFromURL(name, pattern);
1384: } catch (WinstoneException err) {
1385: Logger.log(Logger.WARNING, Launcher.RESOURCES,
1386: "WebAppConfig.ErrorMapURL", err.getMessage());
1387: return;
1388: }
1389:
1390: // put the pattern in the correct list
1391: if (urlPattern.getPatternType() == Mapping.EXACT_PATTERN) {
1392: exactPatterns.put(urlPattern.getUrlPattern(), name);
1393: } else if (urlPattern.getPatternType() == Mapping.FOLDER_PATTERN) {
1394: folderPatterns.add(urlPattern);
1395: } else if (urlPattern.getPatternType() == Mapping.EXTENSION_PATTERN) {
1396: extensionPatterns.add(urlPattern);
1397: } else if (urlPattern.getPatternType() == Mapping.DEFAULT_SERVLET) {
1398: this .defaultServletName = name;
1399: } else {
1400: Logger.log(Logger.WARNING, Launcher.RESOURCES,
1401: "WebAppConfig.InvalidMount", new String[] { name,
1402: pattern });
1403: }
1404: }
1405:
1406: /**
1407: * Execute the pattern match, and try to return a servlet that matches this
1408: * URL
1409: */
1410: private ServletConfiguration urlMatch(String path,
1411: StringBuffer servletPath, StringBuffer pathInfo) {
1412: Logger.log(Logger.FULL_DEBUG, Launcher.RESOURCES,
1413: "WebAppConfig.URLMatch", path);
1414:
1415: // Check exact matches first
1416: String exact = (String) this .exactServletMatchMounts.get(path);
1417: if (exact != null) {
1418: if (this .servletInstances.get(exact) != null) {
1419: servletPath
1420: .append(WinstoneRequest.decodeURLToken(path));
1421: // pathInfo.append(""); // a hack - empty becomes null later
1422: return (ServletConfiguration) this .servletInstances
1423: .get(exact);
1424: }
1425: }
1426:
1427: // Inexact mount check
1428: for (int n = 0; n < this .patternMatches.length; n++) {
1429: Mapping urlPattern = this .patternMatches[n];
1430: if (urlPattern.match(path, servletPath, pathInfo)
1431: && (this .servletInstances.get(urlPattern
1432: .getMappedTo()) != null)) {
1433: return (ServletConfiguration) this .servletInstances
1434: .get(urlPattern.getMappedTo());
1435: }
1436: }
1437:
1438: // return default servlet
1439: // servletPath.append(""); // unneeded
1440: if (this .servletInstances.get(this .defaultServletName) == null)
1441: throw new WinstoneException(Launcher.RESOURCES.getString(
1442: "WebAppConfig.MatchedNonExistServlet",
1443: this .defaultServletName));
1444: // pathInfo.append(path);
1445: servletPath.append(WinstoneRequest.decodeURLToken(path));
1446: return (ServletConfiguration) this .servletInstances
1447: .get(this .defaultServletName);
1448: }
1449:
1450: /**
1451: * Constructs a session instance with the given sessionId
1452: *
1453: * @param sessionId The sessionID for the new session
1454: * @return A valid session object
1455: */
1456: public WinstoneSession makeNewSession(String sessionId) {
1457: WinstoneSession ws = new WinstoneSession(sessionId);
1458: ws.setWebAppConfiguration(this );
1459: setSessionListeners(ws);
1460: if ((this .sessionTimeout != null)
1461: && (this .sessionTimeout.intValue() > 0)) {
1462: ws
1463: .setMaxInactiveInterval(this .sessionTimeout
1464: .intValue() * 60);
1465: } else {
1466: ws.setMaxInactiveInterval(-1);
1467: }
1468: ws.setLastAccessedDate(System.currentTimeMillis());
1469: ws.sendCreatedNotifies();
1470: this .sessions.put(sessionId, ws);
1471: return ws;
1472: }
1473:
1474: /**
1475: * Retrieves the session by id. If the web app is distributable, it asks the
1476: * other members of the cluster if it doesn't have it itself.
1477: *
1478: * @param sessionId The id of the session we want
1479: * @return A valid session instance
1480: */
1481: public WinstoneSession getSessionById(String sessionId,
1482: boolean localOnly) {
1483: if (sessionId == null) {
1484: return null;
1485: }
1486: WinstoneSession session = (WinstoneSession) this .sessions
1487: .get(sessionId);
1488: if (session != null) {
1489: return session;
1490: }
1491:
1492: // If I'm distributable ... check remotely
1493: if ((this .cluster != null) && !localOnly) {
1494: session = this .cluster
1495: .askClusterForSession(sessionId, this );
1496: if (session != null) {
1497: this .sessions.put(sessionId, session);
1498: }
1499: return session;
1500: } else {
1501: return null;
1502: }
1503: }
1504:
1505: /**
1506: * Add/Remove the session from the collection
1507: */
1508: void removeSessionById(String sessionId) {
1509: this .sessions.remove(sessionId);
1510: }
1511:
1512: void addSession(String sessionId, WinstoneSession session) {
1513: this .sessions.put(sessionId, session);
1514: }
1515:
1516: public void invalidateExpiredSessions() {
1517: Object allSessions[] = this .sessions.values().toArray();
1518: int expiredCount = 0;
1519:
1520: for (int n = 0; n < allSessions.length; n++) {
1521: WinstoneSession session = (WinstoneSession) allSessions[n];
1522: if (!session.isNew() && session.isUnusedByRequests()
1523: && session.isExpired()) {
1524: session.invalidate();
1525: expiredCount++;
1526: }
1527: }
1528: if (expiredCount > 0) {
1529: Logger.log(Logger.DEBUG, Launcher.RESOURCES,
1530: "WebAppConfig.InvalidatedSessions", expiredCount
1531: + "");
1532: }
1533: }
1534:
1535: public void setSessionListeners(WinstoneSession session) {
1536: session
1537: .setSessionActivationListeners(this .sessionActivationListeners);
1538: session
1539: .setSessionAttributeListeners(this .sessionAttributeListeners);
1540: session.setSessionListeners(this .sessionListeners);
1541: }
1542:
1543: public void removeServletConfigurationAndMappings(
1544: ServletConfiguration config) {
1545: this .servletInstances.remove(config.getServletName());
1546: // The urlMatch method will only match to non-null mappings, so we don't need
1547: // to remove anything here
1548: }
1549:
1550: /***************************************************************************
1551: *
1552: * OK ... from here to the end is the interface implementation methods for
1553: * the servletContext interface.
1554: *
1555: **************************************************************************/
1556:
1557: // Application level attributes
1558: public Object getAttribute(String name) {
1559: return this .attributes.get(name);
1560: }
1561:
1562: public Enumeration getAttributeNames() {
1563: return Collections.enumeration(this .attributes.keySet());
1564: }
1565:
1566: public void removeAttribute(String name) {
1567: Object me = this .attributes.get(name);
1568: this .attributes.remove(name);
1569: if (me != null)
1570: for (int n = 0; n < this .contextAttributeListeners.length; n++) {
1571: ClassLoader cl = Thread.currentThread()
1572: .getContextClassLoader();
1573: Thread.currentThread().setContextClassLoader(
1574: getLoader());
1575: this .contextAttributeListeners[n]
1576: .attributeRemoved(new ServletContextAttributeEvent(
1577: this , name, me));
1578: Thread.currentThread().setContextClassLoader(cl);
1579: }
1580: }
1581:
1582: public void setAttribute(String name, Object object) {
1583: if (object == null) {
1584: removeAttribute(name);
1585: } else {
1586: Object me = this .attributes.get(name);
1587: this .attributes.put(name, object);
1588: if (me != null) {
1589: for (int n = 0; n < this .contextAttributeListeners.length; n++) {
1590: ClassLoader cl = Thread.currentThread()
1591: .getContextClassLoader();
1592: Thread.currentThread().setContextClassLoader(
1593: getLoader());
1594: this .contextAttributeListeners[n]
1595: .attributeReplaced(new ServletContextAttributeEvent(
1596: this , name, me));
1597: Thread.currentThread().setContextClassLoader(cl);
1598: }
1599: } else {
1600: for (int n = 0; n < this .contextAttributeListeners.length; n++) {
1601: ClassLoader cl = Thread.currentThread()
1602: .getContextClassLoader();
1603: Thread.currentThread().setContextClassLoader(
1604: getLoader());
1605: this .contextAttributeListeners[n]
1606: .attributeAdded(new ServletContextAttributeEvent(
1607: this , name, object));
1608: Thread.currentThread().setContextClassLoader(cl);
1609: }
1610: }
1611: }
1612: }
1613:
1614: // Application level init parameters
1615: public String getInitParameter(String name) {
1616: return (String) this .initParameters.get(name);
1617: }
1618:
1619: public Enumeration getInitParameterNames() {
1620: return Collections.enumeration(this .initParameters.keySet());
1621: }
1622:
1623: // Server info
1624: public String getServerInfo() {
1625: return Launcher.RESOURCES.getString("ServerVersion");
1626: }
1627:
1628: public int getMajorVersion() {
1629: return 2;
1630: }
1631:
1632: public int getMinorVersion() {
1633: return 5;
1634: }
1635:
1636: // Weird mostly deprecated crap to do with getting servlet instances
1637: public javax.servlet.ServletContext getContext(String uri) {
1638: return this .ownerHostConfig.getWebAppByURI(uri);
1639: }
1640:
1641: public String getServletContextName() {
1642: return this .displayName;
1643: }
1644:
1645: /**
1646: * Look up the map of mimeType extensions, and return the type that matches
1647: */
1648: public String getMimeType(String fileName) {
1649: int dotPos = fileName.lastIndexOf('.');
1650: if ((dotPos != -1) && (dotPos != fileName.length() - 1)) {
1651: String extension = fileName.substring(dotPos + 1)
1652: .toLowerCase();
1653: String mimeType = (String) this .mimeTypes.get(extension);
1654: return mimeType;
1655: } else
1656: return null;
1657: }
1658:
1659: // Context level log statements
1660: public void log(String message) {
1661: Logger.logDirectMessage(Logger.INFO, this .contextName, message,
1662: null);
1663: }
1664:
1665: public void log(String message, Throwable throwable) {
1666: Logger.logDirectMessage(Logger.ERROR, this .contextName,
1667: message, throwable);
1668: }
1669:
1670: /**
1671: * Named dispatcher - this basically gets us a simple exact dispatcher (no
1672: * url matching, no request attributes and no security)
1673: */
1674: public javax.servlet.RequestDispatcher getNamedDispatcher(
1675: String name) {
1676: ServletConfiguration servlet = (ServletConfiguration) this .servletInstances
1677: .get(name);
1678: if (servlet != null) {
1679: RequestDispatcher rd = new RequestDispatcher(this , servlet);
1680: if (rd != null) {
1681: rd.setForNamedDispatcher(this .filterPatternsForward,
1682: this .filterPatternsInclude);
1683: return rd;
1684: }
1685: }
1686: return null;
1687: }
1688:
1689: /**
1690: * Gets a dispatcher, which sets the request attributes, etc on a
1691: * forward/include. Doesn't execute security though.
1692: */
1693: public javax.servlet.RequestDispatcher getRequestDispatcher(
1694: String uriInsideWebapp) {
1695: if (uriInsideWebapp == null) {
1696: return null;
1697: } else if (!uriInsideWebapp.startsWith("/")) {
1698: return null;
1699: }
1700:
1701: // Parse the url for query string, etc
1702: String queryString = "";
1703: int questionPos = uriInsideWebapp.indexOf('?');
1704: if (questionPos != -1) {
1705: if (questionPos != uriInsideWebapp.length() - 1) {
1706: queryString = uriInsideWebapp
1707: .substring(questionPos + 1);
1708: }
1709: uriInsideWebapp = uriInsideWebapp.substring(0, questionPos);
1710: }
1711:
1712: // Return the dispatcher
1713: StringBuffer servletPath = new StringBuffer();
1714: StringBuffer pathInfo = new StringBuffer();
1715: ServletConfiguration servlet = urlMatch(uriInsideWebapp,
1716: servletPath, pathInfo);
1717: if (servlet != null) {
1718: RequestDispatcher rd = new RequestDispatcher(this , servlet);
1719: if (rd != null) {
1720: rd.setForURLDispatcher(servletPath.toString(), pathInfo
1721: .toString().equals("") ? null : pathInfo
1722: .toString(), queryString, uriInsideWebapp,
1723: this .filterPatternsForward,
1724: this .filterPatternsInclude);
1725: return rd;
1726: }
1727: }
1728: return null;
1729: }
1730:
1731: /**
1732: * Creates the dispatcher that corresponds to a request level dispatch (ie
1733: * the initial entry point). The difference here is that we need to set up
1734: * the dispatcher so that on a forward, it executes the security checks and
1735: * the request filters, while not setting any of the request attributes for
1736: * a forward. Also, we can't return a null dispatcher in error case - instead
1737: * we have to return a dispatcher pre-init'd for showing an error page (eg 404).
1738: * A null dispatcher is interpreted to mean a successful 302 has occurred.
1739: */
1740: public RequestDispatcher getInitialDispatcher(
1741: String uriInsideWebapp, WinstoneRequest request,
1742: WinstoneResponse response) throws IOException {
1743: if (!uriInsideWebapp.equals("")
1744: && !uriInsideWebapp.startsWith("/")) {
1745: return this
1746: .getErrorDispatcherByCode(
1747: HttpServletResponse.SC_BAD_REQUEST,
1748: Launcher.RESOURCES.getString(
1749: "WebAppConfig.InvalidURI",
1750: uriInsideWebapp), null);
1751: } else if (this .contextStartupError != null) {
1752: StringWriter sw = new StringWriter();
1753: PrintWriter pw = new PrintWriter(sw, true);
1754: this .contextStartupError.printStackTrace(pw);
1755: return this .getErrorDispatcherByCode(
1756: HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
1757: Launcher.RESOURCES.getString(
1758: "WebAppConfig.ErrorDuringStartup", sw
1759: .toString()),
1760: this .contextStartupError);
1761: }
1762:
1763: // Parse the url for query string, etc
1764: String queryString = "";
1765: int questionPos = uriInsideWebapp.indexOf('?');
1766: if (questionPos != -1) {
1767: if (questionPos != uriInsideWebapp.length() - 1)
1768: queryString = uriInsideWebapp
1769: .substring(questionPos + 1);
1770: uriInsideWebapp = uriInsideWebapp.substring(0, questionPos);
1771: }
1772:
1773: // Return the dispatcher
1774: StringBuffer servletPath = new StringBuffer();
1775: StringBuffer pathInfo = new StringBuffer();
1776: ServletConfiguration servlet = urlMatch(uriInsideWebapp,
1777: servletPath, pathInfo);
1778: if (servlet != null) {
1779: // If the default servlet was returned, we should check for welcome files
1780: if (servlet.getServletName()
1781: .equals(this .defaultServletName)) {
1782: // Is path a directory ?
1783: String directoryPath = servletPath.toString();
1784: if (directoryPath.endsWith("/")) {
1785: directoryPath = directoryPath.substring(0,
1786: directoryPath.length() - 1);
1787: }
1788: if (directoryPath.startsWith("/")) {
1789: directoryPath = directoryPath.substring(1);
1790: }
1791:
1792: File res = new File(webRoot, directoryPath);
1793: if (res.exists()
1794: && res.isDirectory()
1795: && (request.getMethod().equals("GET") || request
1796: .getMethod().equals("HEAD"))) {
1797: // Check for the send back with slash case
1798: if (!servletPath.toString().endsWith("/")) {
1799: Logger.log(Logger.FULL_DEBUG,
1800: Launcher.RESOURCES,
1801: "WebAppConfig.FoundNonSlashDirectory",
1802: servletPath.toString());
1803: response.sendRedirect(this .prefix
1804: + servletPath.toString()
1805: + pathInfo.toString()
1806: + "/"
1807: + (queryString.equals("") ? "" : "?"
1808: + queryString));
1809: return null;
1810: }
1811:
1812: // Check for welcome files
1813: Logger.log(Logger.FULL_DEBUG, Launcher.RESOURCES,
1814: "WebAppConfig.CheckWelcomeFile",
1815: servletPath.toString()
1816: + pathInfo.toString());
1817: String welcomeFile = matchWelcomeFiles(servletPath
1818: .toString()
1819: + pathInfo.toString(), request, queryString);
1820: if (welcomeFile != null) {
1821: response
1822: .sendRedirect(this .prefix + welcomeFile);
1823: // + servletPath.toString()
1824: // + pathInfo.toString()
1825: // + welcomeFile
1826: // + (queryString.equals("") ? "" : "?" + queryString));
1827: return null;
1828: }
1829: }
1830: }
1831:
1832: RequestDispatcher rd = new RequestDispatcher(this , servlet);
1833: rd.setForInitialDispatcher(servletPath.toString(),
1834: pathInfo.toString().equals("") ? null : pathInfo
1835: .toString(), queryString, uriInsideWebapp,
1836: this .filterPatternsRequest,
1837: this .authenticationHandler);
1838: return rd;
1839: }
1840:
1841: // If we are here, return a 404
1842: return this .getErrorDispatcherByCode(
1843: HttpServletResponse.SC_NOT_FOUND, Launcher.RESOURCES
1844: .getString(
1845: "StaticResourceServlet.PathNotFound",
1846: uriInsideWebapp), null);
1847: }
1848:
1849: /**
1850: * Gets a dispatcher, set up for error dispatch.
1851: */
1852: public RequestDispatcher getErrorDispatcherByClass(
1853: Throwable exception) {
1854:
1855: // Check for exception class match
1856: Class exceptionClasses[] = this .errorPagesByExceptionKeysSorted;
1857: Throwable errWrapper = new ServletException(exception);
1858:
1859: while (errWrapper instanceof ServletException) {
1860: errWrapper = ((ServletException) errWrapper).getRootCause();
1861: if (errWrapper == null) {
1862: break;
1863: }
1864: for (int n = 0; n < exceptionClasses.length; n++) {
1865:
1866: Logger.log(Logger.FULL_DEBUG, Launcher.RESOURCES,
1867: "WinstoneResponse.TestingException",
1868: new String[] {
1869: this .errorPagesByExceptionKeysSorted[n]
1870: .getName(),
1871: errWrapper.getClass().getName() });
1872: if (exceptionClasses[n].isInstance(errWrapper)) {
1873: String errorURI = (String) this .errorPagesByException
1874: .get(exceptionClasses[n]);
1875: if (errorURI != null) {
1876: RequestDispatcher rd = buildErrorDispatcher(
1877: errorURI,
1878: HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
1879: null, errWrapper);
1880: if (rd != null) {
1881: return rd;
1882: }
1883: } else {
1884: Logger
1885: .log(
1886: Logger.WARNING,
1887: Launcher.RESOURCES,
1888: "WinstoneResponse.SkippingException",
1889: new String[] {
1890: exceptionClasses[n]
1891: .getName(),
1892: (String) this .errorPagesByException
1893: .get(exceptionClasses[n]) });
1894: }
1895: } else {
1896: Logger.log(Logger.WARNING, Launcher.RESOURCES,
1897: "WinstoneResponse.ExceptionNotMatched",
1898: exceptionClasses[n].getName());
1899: }
1900: }
1901: }
1902:
1903: // Otherwise throw a code error
1904: Throwable errPassDown = exception;
1905: while ((errPassDown instanceof ServletException)
1906: && (((ServletException) errPassDown).getRootCause() != null)) {
1907: errPassDown = ((ServletException) errPassDown)
1908: .getRootCause();
1909: }
1910: return getErrorDispatcherByCode(
1911: HttpServletResponse.SC_INTERNAL_SERVER_ERROR, null,
1912: errPassDown);
1913: }
1914:
1915: public RequestDispatcher getErrorDispatcherByCode(int statusCode,
1916: String summaryMessage, Throwable exception) {
1917: // Check for status code match
1918: String errorURI = (String) getErrorPagesByCode().get(
1919: "" + statusCode);
1920: if (errorURI != null) {
1921: RequestDispatcher rd = buildErrorDispatcher(errorURI,
1922: statusCode, summaryMessage, exception);
1923: if (rd != null) {
1924: return rd;
1925: }
1926: }
1927:
1928: // If no dispatcher available, return a dispatcher to the default error formatter
1929: ServletConfiguration errorServlet = (ServletConfiguration) this .servletInstances
1930: .get(this .errorServletName);
1931: if (errorServlet != null) {
1932: RequestDispatcher rd = new RequestDispatcher(this ,
1933: errorServlet);
1934: if (rd != null) {
1935: rd.setForErrorDispatcher(null, null, null, statusCode,
1936: summaryMessage, exception, null,
1937: this .filterPatternsError);
1938: return rd;
1939: }
1940: }
1941:
1942: // Otherwise log and return null
1943: Logger.log(Logger.ERROR, Launcher.RESOURCES,
1944: "WebAppConfig.NoErrorServlet", "" + statusCode,
1945: exception);
1946: return null;
1947: }
1948:
1949: /**
1950: * Build a dispatcher to the error handler if it's available. If it fails, return null.
1951: */
1952: private RequestDispatcher buildErrorDispatcher(String errorURI,
1953: int statusCode, String summaryMessage, Throwable exception) {
1954: // Parse the url for query string, etc
1955: String queryString = "";
1956: int questionPos = errorURI.indexOf('?');
1957: if (questionPos != -1) {
1958: if (questionPos != errorURI.length() - 1) {
1959: queryString = errorURI.substring(questionPos + 1);
1960: }
1961: errorURI = errorURI.substring(0, questionPos);
1962: }
1963:
1964: // Get the message by recursing if none supplied
1965: ServletException errIterator = new ServletException(exception);
1966: while ((summaryMessage == null) && (errIterator != null)) {
1967: summaryMessage = errIterator.getMessage();
1968: if (errIterator.getRootCause() instanceof ServletException) {
1969: errIterator = (ServletException) errIterator
1970: .getRootCause();
1971: } else {
1972: if (summaryMessage == null) {
1973: summaryMessage = errIterator.getRootCause()
1974: .getMessage();
1975: }
1976: errIterator = null;
1977: }
1978: }
1979:
1980: // Return the dispatcher
1981: StringBuffer servletPath = new StringBuffer();
1982: StringBuffer pathInfo = new StringBuffer();
1983: ServletConfiguration servlet = urlMatch(errorURI, servletPath,
1984: pathInfo);
1985: if (servlet != null) {
1986: RequestDispatcher rd = new RequestDispatcher(this , servlet);
1987: if (rd != null) {
1988: rd.setForErrorDispatcher(servletPath.toString(),
1989: pathInfo.toString().equals("") ? null
1990: : pathInfo.toString(), queryString,
1991: statusCode, summaryMessage, exception,
1992: errorURI, this .filterPatternsError);
1993: return rd;
1994: }
1995: }
1996: return null;
1997: }
1998:
1999: /**
2000: * Check if any of the welcome files under this path are available. Returns the
2001: * name of the file if found, null otherwise. Returns the full internal webapp uri
2002: */
2003: private String matchWelcomeFiles(String path,
2004: WinstoneRequest request, String queryString) {
2005: if (!path.endsWith("/")) {
2006: path = path + "/";
2007: }
2008:
2009: String qs = (queryString.equals("") ? "" : "?" + queryString);
2010: for (int n = 0; n < this .welcomeFiles.length; n++) {
2011: String welcomeFile = this .welcomeFiles[n];
2012: while (welcomeFile.startsWith("/")) {
2013: welcomeFile = welcomeFile.substring(1);
2014: }
2015: welcomeFile = path + welcomeFile;
2016:
2017: String exact = (String) this .exactServletMatchMounts
2018: .get(welcomeFile);
2019: if (exact != null) {
2020: return welcomeFile + qs;
2021: }
2022:
2023: // Inexact folder mount check - note folder mounts only
2024: for (int j = 0; j < this .patternMatches.length; j++) {
2025: Mapping urlPattern = this .patternMatches[j];
2026: if ((urlPattern.getPatternType() == Mapping.FOLDER_PATTERN)
2027: && urlPattern.match(welcomeFile, null, null)) {
2028: return welcomeFile + qs;
2029: }
2030: }
2031:
2032: try {
2033: if (getResource(welcomeFile) != null) {
2034: return welcomeFile + qs;
2035: }
2036: } catch (MalformedURLException err) {
2037: }
2038: }
2039: return null;
2040: }
2041:
2042: // Getting resources via the classloader
2043: public URL getResource(String path) throws MalformedURLException {
2044: if (path == null) {
2045: return null;
2046: } else if (!path.startsWith("/")) {
2047: throw new MalformedURLException(Launcher.RESOURCES
2048: .getString("WebAppConfig.BadResourcePath", path));
2049: } else if (!path.equals("/") && path.endsWith("/")) {
2050: path = path.substring(0, path.length() - 1);
2051: }
2052: File res = new File(webRoot, path.substring(1));
2053: return (res != null) && res.exists() ? res.toURL() : null;
2054: }
2055:
2056: public InputStream getResourceAsStream(String path) {
2057: try {
2058: URL res = getResource(path);
2059: return res == null ? null : res.openStream();
2060: } catch (IOException err) {
2061: throw new WinstoneException(Launcher.RESOURCES
2062: .getString("WebAppConfig.ErrorOpeningStream"), err);
2063: }
2064: }
2065:
2066: public String getRealPath(String path) {
2067: // Trim the prefix
2068: if (path == null)
2069: return null;
2070: else {
2071: try {
2072: File res = new File(this .webRoot, path);
2073: if (res.isDirectory())
2074: return res.getCanonicalPath() + "/";
2075: else
2076: return res.getCanonicalPath();
2077: } catch (IOException err) {
2078: return null;
2079: }
2080: }
2081: }
2082:
2083: public Set getResourcePaths(String path) {
2084: // Trim the prefix
2085: if (path == null)
2086: return null;
2087: else if (!path.startsWith("/"))
2088: throw new WinstoneException(Launcher.RESOURCES.getString(
2089: "WebAppConfig.BadResourcePath", path));
2090: else {
2091: String workingPath = null;
2092: if (path.equals("/"))
2093: workingPath = "";
2094: else {
2095: boolean lastCharIsSlash = path
2096: .charAt(path.length() - 1) == '/';
2097: workingPath = path.substring(1, path.length()
2098: - (lastCharIsSlash ? 1 : 0));
2099: }
2100: File inPath = new File(this .webRoot,
2101: workingPath.equals("") ? "." : workingPath)
2102: .getAbsoluteFile();
2103: if (!inPath.exists())
2104: return null;
2105: else if (!inPath.isDirectory())
2106: return null;
2107:
2108: // Find all the files in this folder
2109: File children[] = inPath.listFiles();
2110: Set out = new HashSet();
2111: for (int n = 0; n < children.length; n++) {
2112: // Write the entry as subpath + child element
2113: String entry = //this.prefix +
2114: "/"
2115: + (workingPath.length() != 0 ? workingPath
2116: + "/" : "") + children[n].getName()
2117: + (children[n].isDirectory() ? "/" : "");
2118: out.add(entry);
2119: }
2120: return out;
2121: }
2122: }
2123:
2124: /**
2125: * @deprecated
2126: */
2127: public javax.servlet.Servlet getServlet(String name) {
2128: return null;
2129: }
2130:
2131: /**
2132: * @deprecated
2133: */
2134: public Enumeration getServletNames() {
2135: return Collections.enumeration(new ArrayList());
2136: }
2137:
2138: /**
2139: * @deprecated
2140: */
2141: public Enumeration getServlets() {
2142: return Collections.enumeration(new ArrayList());
2143: }
2144:
2145: /**
2146: * @deprecated
2147: */
2148: public void log(Exception exception, String msg) {
2149: this.log(msg, exception);
2150: }
2151:
2152: }
|