--- /dev/null
+/*
+ * Copyright 2009 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.servlet;
+
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+/**
+ * Wraps the list of filters for the current request. One instance
+ * associated with each RequestImpl, reused.
+ *
+ * Populated by the mapper ( WebappFilterMapper for example ), which
+ * determines the filters for the current request.
+ *
+ * Not thread safe.
+ */
+public final class FilterChainImpl implements FilterChain {
+
+ private List<FilterConfigImpl> filters = new ArrayList<FilterConfigImpl>();
+
+ /**
+ * The int which is used to maintain the current position
+ * in the filter chain.
+ */
+ private int pos = 0;
+
+ /**
+ * The servlet instance to be executed by this chain.
+ */
+ private Servlet servlet = null;
+
+
+ private ServletConfigImpl wrapper;
+
+
+ public FilterChainImpl() {
+ super();
+ }
+
+
+ /**
+ * Invoke the next filter in this chain, passing the specified request
+ * and response. If there are no more filters in this chain, invoke
+ * the <code>service()</code> method of the servlet itself.
+ *
+ * @param request The servlet request we are processing
+ * @param response The servlet response we are creating
+ *
+ * @exception IOException if an input/output error occurs
+ * @exception ServletException if a servlet exception occurs
+ */
+ public void doFilter(ServletRequest request, ServletResponse response)
+ throws IOException, ServletException {
+
+
+ // Call the next filter if there is one
+ if (pos < filters.size()) {
+ FilterConfigImpl filterConfig = filters.get(pos++);
+ Filter filter = null;
+ try {
+ filter = filterConfig.getFilter();
+ filter.doFilter(request, response, this);
+ } catch (IOException e) {
+ throw e;
+ } catch (ServletException e) {
+ throw e;
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Throwable e) {
+ e.printStackTrace();
+ throw new ServletException("Throwable", e);
+ }
+ return;
+ }
+
+ // We fell off the end of the chain -- call the servlet instance
+ try {
+ if (servlet != null)
+ servlet.service(request, response);
+ } catch (IOException e) {
+ throw e;
+ } catch (ServletException e) {
+ throw e;
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Throwable e) {
+ throw new ServletException("Throwable", e);
+ }
+ }
+
+
+ // -------------------------------------------------------- Package Methods
+
+
+
+ /**
+ * Add a filter to the set of filters that will be executed in this chain.
+ *
+ * @param filterConfig The FilterConfig for the servlet to be executed
+ */
+ public void addFilter(FilterConfigImpl filterConfig) {
+ filters.add(filterConfig);
+ }
+
+
+ /**
+ * Release references to the filters and wrapper executed by this chain.
+ */
+ public void release() {
+ filters.clear();
+ pos = 0;
+ servlet = null;
+ }
+
+
+ /**
+ * Set the servlet that will be executed at the end of this chain.
+ * Set by the mapper filter
+ */
+ public void setServlet(ServletConfigImpl wrapper, Servlet servlet) {
+ this.wrapper = wrapper;
+ this.servlet = servlet;
+ }
+
+ // ------ Getters for information ------------
+
+ public int getSize() {
+ return filters.size();
+ }
+
+ public FilterConfigImpl getFilter(int i) {
+ return filters.get(i);
+ }
+
+ public Servlet getServlet() {
+ return servlet;
+ }
+
+ public ServletConfigImpl getServletConfig() {
+ return wrapper;
+ }
+
+ public int getPos() {
+ return pos;
+ }
+}
--- /dev/null
+/*
+ * 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.servlet;
+
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+import org.apache.tomcat.servlets.util.Enumerator;
+
+
+/**
+ * A Filter is configured in web.xml by:
+ * - name - used in mappings
+ * - className - used to instantiate the filter
+ * - init params
+ * - other things not used in the servlet container ( icon, descr, etc )
+ *
+ * Alternatively, in API mode you can pass the actual filter.
+ *
+ * @see ServletConfigImpl
+ */
+public class FilterConfigImpl implements FilterConfig {
+
+ public FilterConfigImpl(ServletContextImpl context) {
+ this.ctx = context;
+ }
+
+ boolean asyncSupported;
+
+ ServletContextImpl ctx = null;
+
+ /**
+ * The application Filter we are configured for.
+ */
+ private transient Filter filter = null;
+
+ String descryption;
+
+ private String filterName;
+
+ private String filterClassName;
+
+ Map<String, String> initParams;
+
+ private Class<? extends Filter> filterClass;
+
+ private boolean initDone = false;
+
+ public void setData(String filterName, String filterClass,
+ Map<String, String> params) {
+ this.filterName = filterName;
+ this.filterClassName = filterClass;
+ this.initParams = params;
+ }
+
+ public void setFilter(Filter f) {
+ filter = f;
+ }
+
+ public String getFilterName() {
+ return filterName;
+ }
+
+ public void setFilterClass(Class<? extends Filter> filterClass2) {
+ this.filterClass = filterClass2;
+ }
+
+
+ public String getInitParameter(String name) {
+ if (initParams == null) return null;
+ return initParams.get(name);
+ }
+
+ /**
+ * Return an <code>Enumeration</code> of the names of the initialization
+ * parameters for this Filter.
+ */
+ public Enumeration getInitParameterNames() {
+ if (initParams == null)
+ return (new Enumerator(new ArrayList()));
+ else
+ return (new Enumerator(initParams.keySet()));
+ }
+
+
+ /**
+ * Return the ServletContext of our associated web application.
+ */
+ public ServletContext getServletContext() {
+ return ctx;
+ }
+
+ /**
+ * Return the application Filter we are configured for.
+ */
+ public Filter createFilter() throws ClassCastException, ClassNotFoundException,
+ IllegalAccessException, InstantiationException, ServletException {
+
+ // Return the existing filter instance, if any
+ if (filter != null)
+ return filter;
+
+ ClassLoader classLoader = ctx.getClassLoader();
+
+ ClassLoader oldCtxClassLoader =
+ Thread.currentThread().getContextClassLoader();
+ if (classLoader != oldCtxClassLoader) {
+ Thread.currentThread().setContextClassLoader(classLoader);
+ }
+ try {
+ if (filterClass == null) {
+ filterClass = (Class<? extends Filter>) classLoader.loadClass(filterClassName);
+ }
+ this.filter = (Filter) filterClass.newInstance();
+ } finally {
+ if (classLoader != oldCtxClassLoader) {
+ Thread.currentThread().setContextClassLoader(oldCtxClassLoader);
+ }
+ }
+
+ // TODO: resource injection
+
+ return filter;
+ }
+
+ public Filter getFilter() throws ClassCastException, ClassNotFoundException, IllegalAccessException, InstantiationException, ServletException {
+ Filter filter = createFilter();
+ if (!initDone ) {
+ filter.init(this);
+ initDone = true;
+ }
+ return (this.filter);
+ }
+
+
+ /**
+ * Release the Filter instance associated with this FilterConfig,
+ * if there is one.
+ */
+ public void release() {
+ if (this.filter != null){
+ filter.destroy();
+ }
+ this.filter = null;
+ }
+
+ public boolean setInitParameter(String name, String value)
+ throws IllegalArgumentException, IllegalStateException {
+ return ServletContextImpl.setInitParameter(ctx, initParams,
+ name, value);
+ }
+
+ public Set<String> setInitParameters(Map<String, String> initParameters)
+ throws IllegalArgumentException, IllegalStateException {
+ return ServletContextImpl.setInitParameters(ctx, initParams,
+ initParameters);
+ }
+}
--- /dev/null
+/*
+ */
+package org.apache.tomcat.lite.servlet;
+
+import java.io.IOException;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+import org.apache.tomcat.lite.http.HttpChannel;
+import org.apache.tomcat.lite.http.HttpRequest;
+import org.apache.tomcat.servlets.jsp.BaseJspLoader;
+
+public class JspLoader extends BaseJspLoader {
+
+ public ClassLoader getClassLoader(ServletContext ctx) {
+ return ((ServletContextImpl) ctx).getClassLoader();
+ }
+
+ public String getClassPath(ServletContext ctx) {
+ return ((ServletContextImpl) ctx).getClassPath();
+ }
+
+ protected void compileAndInitPage(ServletContext ctx,
+ String jspUri,
+ ServletConfig cfg,
+ String classPath)
+ throws ServletException, IOException {
+
+ ServletContextImpl ctxI = (ServletContextImpl)ctx;
+ HttpChannel server = ctxI.getEngine().getLocalConnector().getServer();
+
+ HttpRequest req = server.getRequest();
+
+ req.addParameter("uriroot", ctx.getRealPath("/"));
+ req.addParameter("jspFiles", jspUri.substring(1));
+ req.addParameter("classPath", classPath);
+ req.addParameter("pkg", getPackage(ctx, jspUri));
+
+ // TODO: init params to specify
+ // TODO: remote request
+ RequestDispatcher disp = ctx.getNamedDispatcher("jspc");
+
+ ServletRequestImpl sreq =
+ TomcatLite.getFacade(req);
+ sreq.setContext((ServletContextImpl) ctx);
+ disp.forward(sreq, sreq.getResponse());
+ }
+}
--- /dev/null
+/*
+ * 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.servlet;
+
+
+import java.io.InputStream;
+import java.util.Locale;
+import java.util.Properties;
+
+
+
+/**
+ * One instance per Context. Holds the
+ *
+ * Utility class that attempts to map from a Locale to the corresponding
+ * character set to be used for interpreting input text (or generating
+ * output text) when the Content-Type header does not include one. You
+ * can customize the behavior of this class by modifying the mapping data
+ * it loads, or by subclassing it (to change the algorithm) and then using
+ * your own version for a particular web application.
+ *
+ * @author Craig R. McClanahan
+ */
+public class Locale2Charset {
+
+
+ // shared for all instances - they can add more in a copy
+ private static Properties defaultMap = new Properties();
+
+ static {
+ defaultMap.put("en", "ISO-8859-1");
+ }
+
+ Locale2Charset() {
+ map = defaultMap;
+ }
+
+
+ // ---------------------------------------------------- Instance Variables
+
+ /**
+ * The mapping properties that have been initialized from the specified or
+ * default properties resource.
+ */
+ private Properties map;
+
+
+ // ------------------------------------------------------- Public Methods
+
+
+ /**
+ * Calculate the name of a character set to be assumed, given the specified
+ * Locale and the absence of a character set specified as part of the
+ * content type header.
+ *
+ * @param locale The locale for which to calculate a character set
+ */
+ public String getCharset(Locale locale) {
+ // Match full language_country_variant first, then language_country,
+ // then language only
+ String charset = map.getProperty(locale.toString());
+ if (charset == null) {
+ charset = map.getProperty(locale.getLanguage() + "_"
+ + locale.getCountry());
+ if (charset == null) {
+ charset = map.getProperty(locale.getLanguage());
+ }
+ }
+ return (charset);
+ }
+
+
+ /**
+ * The deployment descriptor can have a
+ * locale-encoding-mapping-list element which describes the
+ * webapp's desired mapping from locale to charset. This method
+ * gets called when processing the web.xml file for a context
+ *
+ * @param locale The locale for a character set
+ * @param charset The charset to be associated with the locale
+ */
+ public void addCharsetMapping(String locale, String charset) {
+ if (map == defaultMap) {
+ // new copy, don't modify original
+ map = new Properties(defaultMap);
+ }
+ map.put(locale, charset);
+ }
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.servlet;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletRequestWrapper;
+import javax.servlet.ServletResponse;
+import javax.servlet.ServletResponseWrapper;
+import javax.servlet.UnavailableException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.tomcat.lite.http.MappingData;
+import org.apache.tomcat.lite.io.CBuffer;
+
+
+/**
+ *
+ */
+public final class RequestDispatcherImpl implements RequestDispatcher {
+ /**
+ * The request attribute under which the original servlet path is stored
+ * on an forwarded dispatcher request.
+ */
+ public static final String FORWARD_SERVLET_PATH_ATTR =
+ "javax.servlet.forward.servlet_path";
+
+
+ /**
+ * The request attribute under which the original query string is stored
+ * on an forwarded dispatcher request.
+ */
+ public static final String FORWARD_QUERY_STRING_ATTR =
+ "javax.servlet.forward.query_string";
+
+ /**
+ * The request attribute under which the original request URI is stored
+ * on an forwarded dispatcher request.
+ */
+ public static final String FORWARD_REQUEST_URI_ATTR =
+ "javax.servlet.forward.request_uri";
+
+
+ /**
+ * The request attribute under which the original context path is stored
+ * on an forwarded dispatcher request.
+ */
+ public static final String FORWARD_CONTEXT_PATH_ATTR =
+ "javax.servlet.forward.context_path";
+
+
+ /**
+ * The request attribute under which the original path info is stored
+ * on an forwarded dispatcher request.
+ */
+ public static final String FORWARD_PATH_INFO_ATTR =
+ "javax.servlet.forward.path_info";
+
+ /**
+ * The request attribute under which we store the servlet name on a
+ * named dispatcher request.
+ */
+ public static final String NAMED_DISPATCHER_ATTR =
+ "org.apache.catalina.NAMED";
+
+ /**
+ * The request attribute under which the request URI of the included
+ * servlet is stored on an included dispatcher request.
+ */
+ public static final String INCLUDE_REQUEST_URI_ATTR =
+ "javax.servlet.include.request_uri";
+
+
+ /**
+ * The request attribute under which the context path of the included
+ * servlet is stored on an included dispatcher request.
+ */
+ public static final String INCLUDE_CONTEXT_PATH_ATTR =
+ "javax.servlet.include.context_path";
+
+
+ /**
+ * The request attribute under which the path info of the included
+ * servlet is stored on an included dispatcher request.
+ */
+ public static final String INCLUDE_PATH_INFO_ATTR =
+ "javax.servlet.include.path_info";
+
+
+ /**
+ * The request attribute under which the servlet path of the included
+ * servlet is stored on an included dispatcher request.
+ */
+ public static final String INCLUDE_SERVLET_PATH_ATTR =
+ "javax.servlet.include.servlet_path";
+
+
+ /**
+ * The request attribute under which the query string of the included
+ * servlet is stored on an included dispatcher request.
+ */
+ public static final String INCLUDE_QUERY_STRING_ATTR =
+ "javax.servlet.include.query_string";
+
+ /**
+ * The request attribute under which we expose the value of the
+ * <code><jsp-file></code> value associated with this servlet,
+ * if any.
+ */
+ public static final String JSP_FILE_ATTR =
+ "org.apache.catalina.jsp_file";
+
+
+ // ----------------------------------------------------- Instance Variables
+
+ private static Logger log = Logger.getLogger(RequestDispatcherImpl.class.getName());
+
+ private ServletContextImpl ctx = null;
+
+ /**
+ * The servlet name for a named dispatcher.
+ */
+ private String name = null;
+
+ // Path for a path dispatcher
+ private String path;
+
+ /**
+ * MappingData object - per thread for buffering.
+ */
+ private transient ThreadLocal<MappingData> localMappingData =
+ new ThreadLocal<MappingData>();
+
+ /*
+ OrigRequest(ServletRequestImpl) -> include/forward * -> this include
+
+ On the path: user-defined RequestWrapper or our ServletRequestWrapper
+
+ include() is called with a RequestWrapper(->...->origRequest) or origRequest
+
+ Based on params, etc -> we wrap the req / response in ServletRequestWrapper,
+ call filters+servlet. Inside, the req can be wrapped again in
+ userReqWrapper, and other include called.
+
+
+ */
+
+ /**
+ * The outermost request that will be passed on to the invoked servlet.
+ */
+ private ServletRequest outerRequest = null;
+
+ /**
+ * The outermost response that will be passed on to the invoked servlet.
+ */
+ private ServletResponse outerResponse = null;
+
+ /**
+ * The request wrapper we have created and installed (if any).
+ */
+ private ServletRequest wrapRequest = null;
+
+ /**
+ * The response wrapper we have created and installed (if any).
+ */
+ private ServletResponse wrapResponse = null;
+
+ // Parameters used when constructing the dispatcvher
+ /**
+ * The extra path information for this RequestDispatcher.
+ */
+ private String pathInfo = null;
+ /**
+ * The query string parameters for this RequestDispatcher.
+ */
+ private String queryString = null;
+ /**
+ * The request URI for this RequestDispatcher.
+ */
+ private String requestURI = null;
+ /**
+ * The servlet path for this RequestDispatcher.
+ */
+ private String servletPath = null;
+
+ //
+ private String origServletPath = null;
+
+ /**
+ * The Wrapper associated with the resource that will be forwarded to
+ * or included.
+ */
+ private ServletConfigImpl wrapper = null;
+
+ private Servlet servlet;
+
+ /** Named dispatcher
+ */
+ public RequestDispatcherImpl(ServletConfigImpl wrapper, String name) {
+ this.wrapper = wrapper;
+ this.name = name;
+ this.ctx = (ServletContextImpl) wrapper.getServletContext();
+
+ }
+
+ public RequestDispatcherImpl(ServletContextImpl ctx, String path) {
+ this.path = path;
+ this.ctx = ctx;
+ }
+
+
+
+ /**
+ * Forward this request and response to another resource for processing.
+ * Any runtime exception, IOException, or ServletException thrown by the
+ * called servlet will be propogated to the caller.
+ *
+ * @param request The servlet request to be forwarded
+ * @param response The servlet response to be forwarded
+ *
+ * @exception IOException if an input/output error occurs
+ * @exception ServletException if a servlet exception occurs
+ */
+ public void forward(ServletRequest request, ServletResponse response)
+ throws ServletException, IOException
+ {
+ // Reset any output that has been buffered, but keep headers/cookies
+ if (response.isCommitted()) {
+ throw new IllegalStateException("forward(): response.isComitted()");
+ }
+
+ try {
+ response.resetBuffer();
+ } catch (IllegalStateException e) {
+ throw e;
+ }
+
+ // Set up to handle the specified request and response
+ setup(request, response, false);
+
+ // Identify the HTTP-specific request and response objects (if any)
+ HttpServletRequest hrequest = (HttpServletRequest) request;
+
+ ServletRequestWrapperImpl wrequest =
+ (ServletRequestWrapperImpl) wrapRequest();
+
+
+ if (name != null) {
+ wrequest.setRequestURI(hrequest.getRequestURI());
+ wrequest.setContextPath(hrequest.getContextPath());
+ wrequest.setServletPath(hrequest.getServletPath());
+ wrequest.setPathInfo(hrequest.getPathInfo());
+ wrequest.setQueryString(hrequest.getQueryString());
+
+
+ } else { // path based
+ mapPath();
+ if (wrapper == null) {
+ throw new ServletException("Forward not found " +
+ path);
+ }
+ String contextPath = ctx.getContextPath();
+ if (hrequest.getAttribute(FORWARD_REQUEST_URI_ATTR) == null) {
+ wrequest.setAttribute(FORWARD_REQUEST_URI_ATTR,
+ hrequest.getRequestURI());
+ wrequest.setAttribute(FORWARD_CONTEXT_PATH_ATTR,
+ hrequest.getContextPath());
+ wrequest.setAttribute(FORWARD_SERVLET_PATH_ATTR,
+ hrequest.getServletPath());
+ wrequest.setAttribute(FORWARD_PATH_INFO_ATTR,
+ hrequest.getPathInfo());
+ wrequest.setAttribute(FORWARD_QUERY_STRING_ATTR,
+ hrequest.getQueryString());
+ }
+
+ wrequest.setContextPath(contextPath);
+ wrequest.setRequestURI(requestURI);
+ wrequest.setServletPath(servletPath);
+ wrequest.setPathInfo(pathInfo);
+ if (queryString != null) {
+ wrequest.setQueryString(queryString);
+ wrequest.setQueryParams(queryString);
+ }
+ }
+ processRequest(outerRequest, outerResponse);
+
+ wrequest.recycle();
+ unwrapRequest();
+
+ // This is not a real close in order to support error processing
+// if ( log.isDebugEnabled() )
+// log.debug(" Disabling the response for futher output");
+
+ if (response instanceof ServletResponseImpl) {
+ ((ServletResponseImpl) response).flushBuffer();
+ ((ServletResponseImpl) response).setSuspended(true);
+ } else {
+ // Servlet SRV.6.2.2. The Resquest/Response may have been wrapped
+ // and may no longer be instance of RequestFacade
+ if (log.isLoggable(Level.FINE)){
+ log.fine( " The Response is vehiculed using a wrapper: "
+ + response.getClass().getName() );
+ }
+
+ // Close anyway
+ try {
+ PrintWriter writer = response.getWriter();
+ writer.close();
+ } catch (IllegalStateException e) {
+ try {
+ ServletOutputStream stream = response.getOutputStream();
+ stream.close();
+ } catch (IllegalStateException f) {
+ ;
+ } catch (IOException f) {
+ ;
+ }
+ } catch (IOException e) {
+ ;
+ }
+ }
+ }
+
+
+
+ /**
+ * Include the response from another resource in the current response.
+ * Any runtime exception, IOException, or ServletException thrown by the
+ * called servlet will be propogated to the caller.
+ *
+ * @param request The servlet request that is including this one
+ * @param response The servlet response to be appended to
+ *
+ * @exception IOException if an input/output error occurs
+ * @exception ServletException if a servlet exception occurs
+ */
+ public void include(ServletRequest request, ServletResponse response)
+ throws ServletException, IOException
+ {
+
+ // Set up to handle the specified request and response
+ setup(request, response, true);
+
+ // Create a wrapped response to use for this request
+ // this actually gets inserted somewhere in the chain - it's not
+ // the last one, but first non-user response
+ wrapResponse();
+ ServletRequestWrapperImpl wrequest =
+ (ServletRequestWrapperImpl) wrapRequest();
+
+
+ // Handle an HTTP named dispatcher include
+ if (name != null) {
+ wrequest.setAttribute(NAMED_DISPATCHER_ATTR, name);
+ if (servletPath != null) wrequest.setServletPath(servletPath);
+ wrequest.setAttribute(WebappFilterMapper.DISPATCHER_TYPE_ATTR,
+ new Integer(WebappFilterMapper.INCLUDE));
+ wrequest.setAttribute(WebappFilterMapper.DISPATCHER_REQUEST_PATH_ATTR,
+ origServletPath);
+ } else {
+ mapPath();
+ String contextPath = ctx.getContextPath();
+ if (requestURI != null)
+ wrequest.setAttribute(INCLUDE_REQUEST_URI_ATTR,
+ requestURI);
+ if (contextPath != null)
+ wrequest.setAttribute(INCLUDE_CONTEXT_PATH_ATTR,
+ contextPath);
+ if (servletPath != null)
+ wrequest.setAttribute(INCLUDE_SERVLET_PATH_ATTR,
+ servletPath);
+ if (pathInfo != null)
+ wrequest.setAttribute(INCLUDE_PATH_INFO_ATTR,
+ pathInfo);
+ if (queryString != null) {
+ wrequest.setAttribute(INCLUDE_QUERY_STRING_ATTR,
+ queryString);
+ wrequest.setQueryParams(queryString);
+ }
+
+ wrequest.setAttribute(WebappFilterMapper.DISPATCHER_TYPE_ATTR,
+ new Integer(WebappFilterMapper.INCLUDE));
+ wrequest.setAttribute(WebappFilterMapper.DISPATCHER_REQUEST_PATH_ATTR,
+ origServletPath);
+ }
+
+ invoke(outerRequest, outerResponse);
+
+ wrequest.recycle();
+ unwrapRequest();
+ unwrapResponse();
+ }
+
+
+ // -------------------------------------------------------- Private Methods
+
+ public void mapPath() {
+ if (path == null || servletPath != null) return;
+
+ // Retrieve the thread local URI, used for mapping
+ // TODO: recycle RequestDispatcher stack and associated objects
+ // instead of this object
+
+ // Retrieve the thread local mapping data
+ MappingData mappingData = (MappingData) localMappingData.get();
+ if (mappingData == null) {
+ mappingData = new MappingData();
+ localMappingData.set(mappingData);
+ }
+
+ // Get query string
+ int pos = path.indexOf('?');
+ if (pos >= 0) {
+ queryString = path.substring(pos + 1);
+ } else {
+ pos = path.length();
+ }
+
+ // Map the URI
+ CBuffer uriMB = CBuffer.newInstance();
+ //mappingData.localURIBytes;
+ uriMB.recycle();
+ //CharChunk uriCC = uriMB.getCharChunk();
+ try {
+ /*
+ * Ignore any trailing path params (separated by ';') for mapping
+ * purposes.
+ * This is sometimes broken - path params can be on any path
+ * component, not just last.
+ */
+ int semicolon = path.indexOf(';');
+ if (pos >= 0 && semicolon > pos) {
+ semicolon = -1;
+ }
+ if (ctx.getContextPath().length() > 1 ) {
+ uriMB.append(ctx.getContextPath());
+ }
+ uriMB.append(path, 0,
+ semicolon > 0 ? semicolon : pos);
+
+ // TODO: make charBuffer part of request or something
+ ctx.getEngine().getDispatcher().map(ctx.getContextMap(), uriMB, mappingData);
+
+ // at least default wrapper must be returned
+
+// /*
+// * Append any trailing path params (separated by ';') that were
+// * ignored for mapping purposes, so that they're reflected in the
+// * RequestDispatcher's requestURI
+// */
+// if (semicolon > 0) {
+// // I don't think this will be used in future
+// charBuffer.append(path,
+// semicolon, pos - semicolon);
+// }
+ } catch (Exception e) {
+ log.log(Level.SEVERE, "getRequestDispatcher()", e);
+ }
+
+ wrapper = (ServletConfigImpl) mappingData.getServiceObject();
+ servletPath = mappingData.wrapperPath.toString();
+ pathInfo = mappingData.pathInfo.toString();
+
+ mappingData.recycle();
+
+ }
+
+
+ /**
+ * Prepare the request based on the filter configuration.
+ * @param request The servlet request we are processing
+ * @param response The servlet response we are creating
+ *
+ * @exception IOException if an input/output error occurs
+ * @exception ServletException if a servlet error occurs
+ */
+ private void processRequest(ServletRequest request,
+ ServletResponse response)
+ throws IOException, ServletException {
+ Integer disInt =
+ (Integer) request.getAttribute(WebappFilterMapper.DISPATCHER_TYPE_ATTR);
+ if (disInt != null) {
+ if (disInt.intValue() != WebappFilterMapper.ERROR) {
+ outerRequest.setAttribute
+ (WebappFilterMapper.DISPATCHER_REQUEST_PATH_ATTR,
+ origServletPath);
+ outerRequest.setAttribute
+ (WebappFilterMapper.DISPATCHER_TYPE_ATTR,
+ new Integer(WebappFilterMapper.FORWARD));
+ }
+ invoke(outerRequest, response);
+ }
+
+ }
+
+
+
+
+ /**
+ * Ask the resource represented by this RequestDispatcher to process
+ * the associated request, and create (or append to) the associated
+ * response.
+ * <p>
+ * <strong>IMPLEMENTATION NOTE</strong>: This implementation assumes
+ * that no filters are applied to a forwarded or included resource,
+ * because they were already done for the original request.
+ *
+ * @param request The servlet request we are processing
+ * @param response The servlet response we are creating
+ *
+ * @exception IOException if an input/output error occurs
+ * @exception ServletException if a servlet error occurs
+ */
+ private void invoke(ServletRequest request, ServletResponse response)
+ throws IOException, ServletException {
+
+ // Checking to see if the context classloader is the current context
+ // classloader. If it's not, we're saving it, and setting the context
+ // classloader to the Context classloader
+ ClassLoader oldCCL = Thread.currentThread().getContextClassLoader();
+ ClassLoader contextClassLoader = ctx.getClassLoader();
+
+ if (oldCCL != contextClassLoader) {
+ Thread.currentThread().setContextClassLoader(contextClassLoader);
+ } else {
+ oldCCL = null;
+ }
+
+ // Initialize local variables we may need
+ HttpServletResponse hresponse = (HttpServletResponse) response;
+ IOException ioException = null;
+ ServletException servletException = null;
+ RuntimeException runtimeException = null;
+
+ servletException = allocateServlet(hresponse, servletException);
+
+ // Get the FilterChain Here
+ WebappFilterMapper factory =
+ ((ServletContextImpl)wrapper.getServletContext()).getFilterMapper();
+
+ FilterChainImpl filterChain = factory.createFilterChain(request,
+ wrapper,
+ servlet);
+
+ // Call the service() method for the allocated servlet instance
+ try {
+ String jspFile = wrapper.getJspFile();
+ if (jspFile != null)
+ request.setAttribute(JSP_FILE_ATTR, jspFile);
+ else
+ request.removeAttribute(JSP_FILE_ATTR);
+ // for includes/forwards
+ if ((servlet != null) && (filterChain != null)) {
+ filterChain.doFilter(request, response);
+ }
+ } catch (IOException e) {
+ ctx.getLogger().log(Level.WARNING, "RequestDispatcherImpl error " +
+ wrapper.getServletName(), e);
+ ioException = e;
+ } catch (UnavailableException e) {
+ ctx.getLogger().log(Level.WARNING, "RequestDispatcherImpl error " +
+ wrapper.getServletName(), e);
+ servletException = e;
+ wrapper.unavailable(e);
+ } catch (ServletException e) {
+ servletException = e;
+ } catch (RuntimeException e) {
+ ctx.getLogger().log(Level.WARNING, "RequestDispatcherImpl error " +
+ wrapper.getServletName(), e);
+ runtimeException = e;
+ }
+ request.removeAttribute(JSP_FILE_ATTR);
+
+ // Release the filter chain (if any) for this request
+ if (filterChain != null)
+ filterChain.release();
+
+ servletException = servletDealocate(servletException);
+
+ // Reset the old context class loader
+ if (oldCCL != null)
+ Thread.currentThread().setContextClassLoader(oldCCL);
+
+ // Unwrap request/response if needed
+ unwrapRequest();
+ unwrapResponse();
+
+ // Rethrow an exception if one was thrown by the invoked servlet
+ if (ioException != null)
+ throw ioException;
+ if (servletException != null)
+ throw servletException;
+ if (runtimeException != null)
+ throw runtimeException;
+
+ }
+
+ private ServletException servletDealocate(ServletException servletException)
+ {
+ if (servlet != null) {
+ wrapper.deallocate(servlet);
+ }
+ return servletException;
+ }
+
+ private ServletException allocateServlet(HttpServletResponse hresponse,
+ ServletException servletException)
+ throws IOException
+ {
+ boolean unavailable = false;
+
+ // Check for the servlet being marked unavailable
+ if (wrapper.isUnavailable()) {
+ ctx.getLogger().log(Level.WARNING, "isUnavailable() " + wrapper.getServletName());
+ long available = wrapper.getAvailable();
+ if ((available > 0L) && (available < Long.MAX_VALUE))
+ hresponse.setDateHeader("Retry-After", available);
+ hresponse.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
+ "Unavailable"); // No need to include internal info: wrapper.getServletName();
+ unavailable = true;
+ }
+
+ // Allocate a servlet instance to process this request
+ try {
+ if (!unavailable) {
+ servlet = wrapper.allocate();
+ }
+ } catch (ServletException e) {
+ ctx.getLogger().log(Level.WARNING, "RequestDispatcher: allocate " +
+ wrapper.toString());
+ servletException = e;
+ servlet = null;
+ } catch (Throwable e) {
+ ctx.getLogger().log(Level.WARNING, "allocate() error " + wrapper.getServletName(), e);
+ servletException = new ServletException
+ ("Allocate error " + wrapper.getServletName(), e);
+ servlet = null;
+ }
+ return servletException;
+ }
+
+
+ /**
+ * Set up to handle the specified request and response
+ *
+ * @param request The servlet request specified by the caller
+ * @param response The servlet response specified by the caller
+ * @param including Are we performing an include() as opposed to
+ * a forward()?
+ */
+ private void setup(ServletRequest request, ServletResponse response,
+ boolean including) {
+
+ this.outerRequest = request;
+ this.outerResponse = response;
+ }
+
+
+ /**
+ * Unwrap the request if we have wrapped it. Not sure how it could end
+ * up in the middle.
+ */
+ private void unwrapRequest() {
+ if (wrapRequest == null)
+ return;
+
+ ServletRequest previous = null;
+ ServletRequest current = outerRequest;
+ while (current != null) {
+ // If we run into the container request we are done
+ if (current instanceof ServletRequestImpl)
+ break;
+
+ // Remove the current request if it is our wrapper
+ if (current == wrapRequest) {
+ ServletRequest next =
+ ((ServletRequestWrapper) current).getRequest();
+ if (previous == null)
+ outerRequest = next;
+ else
+ ((ServletRequestWrapper) previous).setRequest(next);
+ break;
+ }
+
+ // Advance to the next request in the chain
+ previous = current;
+ current = ((ServletRequestWrapper) current).getRequest();
+ }
+ }
+
+
+ /**
+ * Unwrap the response if we have wrapped it.
+ */
+ private void unwrapResponse() {
+ if (wrapResponse == null)
+ return;
+
+ ServletResponse previous = null;
+ ServletResponse current = outerResponse;
+ while (current != null) {
+ // If we run into the container response we are done
+ if (current instanceof ServletResponseImpl)
+ break;
+
+ // Remove the current response if it is our wrapper
+ if (current == wrapResponse) {
+ ServletResponse next =
+ ((ServletResponseWrapper) current).getResponse();
+ if (previous == null)
+ outerResponse = next;
+ else
+ ((ServletResponseWrapper) previous).setResponse(next);
+ break;
+ }
+ // Advance to the next response in the chain
+ previous = current;
+ current = ((ServletResponseWrapper) current).getResponse();
+ }
+ }
+
+
+ /**
+ * Create and return a request wrapper that has been inserted in the
+ * appropriate spot in the request chain.
+ */
+ private ServletRequest wrapRequest() {
+ // Locate the request we should insert in front of
+ ServletRequest previous = null;
+ ServletRequest current = outerRequest;
+ while (current != null) {
+ if (!(current instanceof ServletRequestWrapper))
+ break;
+ if (current instanceof ServletRequestWrapperImpl)
+ break;
+ if (current instanceof ServletRequestImpl)
+ break;
+ // user-specified
+ previous = current;
+ current = ((ServletRequestWrapper) current).getRequest();
+ }
+ // now previous will be a user-specified wrapper,
+ // and current one of our own wrappers ( deeper in stack )
+ // ... current USER_previous USER USER
+ // previous is null if the top request is ours.
+
+ // Instantiate a new wrapper at this point and insert it in the chain
+ ServletRequest wrapper = null;
+
+ // Compute a crossContext flag
+ boolean crossContext = isCrossContext();
+ wrapper =
+ new ServletRequestWrapperImpl((HttpServletRequest) current,
+ ctx, crossContext);
+
+ if (previous == null) {
+ // outer becomes the wrapper, includes orig wrapper inside
+ outerRequest = wrapper;
+ } else {
+ // outer remains user-specified sersvlet, delegating to
+ // our wrapper, which delegates to real request or our wrapper.
+ ((ServletRequestWrapper) previous).setRequest(wrapper);
+ }
+ wrapRequest = wrapper;
+ return (wrapper);
+ }
+
+ private boolean isCrossContext() {
+ boolean crossContext = false;
+ if ((outerRequest instanceof ServletRequestWrapperImpl) ||
+ (outerRequest instanceof ServletRequestImpl) ||
+ (outerRequest instanceof HttpServletRequest)) {
+ HttpServletRequest houterRequest =
+ (HttpServletRequest) outerRequest;
+ Object contextPath =
+ houterRequest.getAttribute(INCLUDE_CONTEXT_PATH_ATTR);
+ if (contextPath == null) {
+ // Forward
+ contextPath = houterRequest.getContextPath();
+ }
+ crossContext = !(ctx.getContextPath().equals(contextPath));
+ }
+ return crossContext;
+ }
+
+
+ /**
+ * Create and return a response wrapper that has been inserted in the
+ * appropriate spot in the response chain.
+ *
+ * Side effect: updates outerResponse, wrapResponse.
+ * The chain is updated with a wrapper below lowest user wrapper
+ */
+ private ServletResponse wrapResponse() {
+ // Locate the response we should insert in front of
+ ServletResponse previous = null;
+ ServletResponse current = outerResponse;
+ while (current != null) {
+ if (!(current instanceof ServletResponseWrapper))
+ break;
+ if (current instanceof ServletResponseImpl)
+ break;
+ previous = current;
+ current = ((ServletResponseWrapper) current).getResponse();
+ }
+
+ // Instantiate a new wrapper at this point and insert it in the chain
+ ServletResponse wrapper =
+ new ServletResponseIncludeWrapper(current);
+
+ if (previous == null) {
+ // outer is ours, we can wrap on top
+ outerResponse = wrapper;
+ } else {
+ // outer is user-specified, leave it alone.
+ // we insert ourself below the lowest user-specified response
+ ((ServletResponseWrapper) previous).setResponse(wrapper);
+ }
+ wrapResponse = wrapper;
+ return (wrapper);
+
+ }
+
+
+}
--- /dev/null
+/*
+ */
+package org.apache.tomcat.lite.servlet;
+
+import org.apache.tomcat.lite.http.HttpRequest;
+
+public class ServletApi {
+
+ public ServletContextImpl newContext() {
+ return null;
+ }
+
+ public ServletRequestImpl newRequest(HttpRequest req) {
+ return null;
+ }
+
+
+ public static ServletApi get() {
+ Class<?> cls = null;
+ try {
+ Class.forName("javax.servlet.http.Part");
+ cls = Class.forName("org.apache.tomcat.lite.servlet.ServletApi30");
+ } catch (Throwable t) {
+ try {
+ cls = Class.forName("org.apache.tomcat.lite.servlet.ServletApi25");
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("Can't load servlet api", e);
+ }
+ }
+ try {
+ return (ServletApi) cls.newInstance();
+ } catch (Throwable e) {
+ throw new RuntimeException("Can't load servlet api", e);
+ }
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ */
+package org.apache.tomcat.lite.servlet;
+
+import org.apache.tomcat.lite.http.HttpRequest;
+
+public class ServletApi25 extends ServletApi {
+ public ServletContextImpl newContext() {
+ return new ServletContextImpl() {
+
+ };
+ }
+
+ public ServletRequestImpl newRequest(HttpRequest req) {
+ return new ServletRequestImpl(req) {
+
+ };
+ }
+}
--- /dev/null
+/*
+ */
+package org.apache.tomcat.lite.servlet;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.naming.NamingException;
+import javax.servlet.AsyncContext;
+import javax.servlet.DispatcherType;
+import javax.servlet.Filter;
+import javax.servlet.FilterRegistration;
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRegistration;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.SessionCookieConfig;
+import javax.servlet.SessionTrackingMode;
+import javax.servlet.FilterRegistration.Dynamic;
+import javax.servlet.descriptor.JspConfigDescriptor;
+import javax.servlet.http.Part;
+
+import org.apache.tomcat.InstanceManager;
+import org.apache.tomcat.integration.ObjectManager;
+import org.apache.tomcat.lite.http.HttpRequest;
+
+public class ServletApi30 extends ServletApi {
+ public ServletContextImpl newContext() {
+ return new ServletContextImpl() {
+ protected void initEngineDefaults() throws ServletException {
+ super.initEngineDefaults();
+ setAttribute(InstanceManager.class.getName(),
+ new LiteInstanceManager(getObjectManager()));
+ }
+ @Override
+ public Dynamic addFilter(String filterName, String className) {
+ FilterConfigImpl fc = new FilterConfigImpl(this);
+ fc.setData(filterName, null, new HashMap());
+ fc.setData(filterName, className, new HashMap());
+ filters.put(filterName, fc);
+ return new DynamicFilterRegistration(fc);
+ }
+
+ @Override
+ public Dynamic addFilter(String filterName, Filter filter) {
+ FilterConfigImpl fc = new FilterConfigImpl(this);
+ fc.setData(filterName, null, new HashMap());
+ fc.setFilter(filter);
+ filters.put(filterName, fc);
+ return new DynamicFilterRegistration(fc);
+ }
+
+ @Override
+ public Dynamic addFilter(String filterName,
+ Class<? extends Filter> filterClass) {
+ FilterConfigImpl fc = new FilterConfigImpl(this);
+ fc.setData(filterName, null, new HashMap());
+ fc.setFilterClass(filterClass);
+ filters.put(filterName, fc);
+ return new DynamicFilterRegistration(fc);
+ }
+
+ @Override
+ public javax.servlet.ServletRegistration.Dynamic addServlet(
+ String servletName, String className) {
+ return null;
+ }
+
+ @Override
+ public javax.servlet.ServletRegistration.Dynamic addServlet(
+ String servletName, Servlet servlet) {
+ return null;
+ }
+
+ @Override
+ public javax.servlet.ServletRegistration.Dynamic addServlet(
+ String servletName, Class<? extends Servlet> servletClass) {
+ return null;
+ }
+
+
+ @Override
+ public Set<SessionTrackingMode> getDefaultSessionTrackingModes() {
+ return null;
+ }
+
+
+ @Override
+ public Set<SessionTrackingMode> getEffectiveSessionTrackingModes() {
+ return null;
+ }
+
+ @Override
+ public FilterRegistration getFilterRegistration(String filterName) {
+ return null;
+ }
+
+ @Override
+ public Map<String, ? extends FilterRegistration> getFilterRegistrations() {
+ return null;
+ }
+
+ @Override
+ public JspConfigDescriptor getJspConfigDescriptor() {
+ return null;
+ }
+
+ @Override
+ public ServletRegistration getServletRegistration(String servletName) {
+ return null;
+ }
+
+ @Override
+ public Map<String, ? extends ServletRegistration> getServletRegistrations() {
+ return null;
+ }
+
+ @Override
+ public SessionCookieConfig getSessionCookieConfig() {
+ return null;
+ }
+
+ @Override
+ public void setSessionTrackingModes(
+ EnumSet<SessionTrackingMode> sessionTrackingModes)
+ throws IllegalStateException, IllegalArgumentException {
+ }
+
+ public int getMajorVersion() {
+ return 3;
+ }
+
+ public int getMinorVersion() {
+ return 0;
+ }
+
+ };
+ }
+
+ public ServletRequestImpl newRequest(HttpRequest req) {
+ return new ServletRequestImpl(req) {
+
+ @Override
+ public Part getPart(String name) {
+ return null;
+ }
+
+ @Override
+ public Collection<Part> getParts() throws IOException,
+ ServletException {
+ return null;
+ }
+
+ @Override
+ public AsyncContext getAsyncContext() {
+ return null;
+ }
+
+ @Override
+ public DispatcherType getDispatcherType() {
+ return null;
+ }
+
+ @Override
+ public AsyncContext startAsync() {
+ return null;
+ }
+
+ @Override
+ public AsyncContext startAsync(ServletRequest servletRequest,
+ ServletResponse servletResponse) {
+ return null;
+ }
+
+ };
+ }
+
+ private final class LiteInstanceManager implements InstanceManager {
+ private ObjectManager om;
+
+ public LiteInstanceManager(ObjectManager objectManager) {
+ this.om = objectManager;
+ }
+
+ @Override
+ public void destroyInstance(Object o)
+ throws IllegalAccessException,
+ InvocationTargetException {
+ }
+
+ @Override
+ public Object newInstance(String className)
+ throws IllegalAccessException,
+ InvocationTargetException, NamingException,
+ InstantiationException,
+ ClassNotFoundException {
+ return om.get(className);
+ }
+
+ @Override
+ public Object newInstance(String fqcn,
+ ClassLoader classLoader)
+ throws IllegalAccessException,
+ InvocationTargetException, NamingException,
+ InstantiationException,
+ ClassNotFoundException {
+ return om.get(fqcn);
+ }
+
+ @Override
+ public void newInstance(Object o)
+ throws IllegalAccessException,
+ InvocationTargetException, NamingException {
+ om.bind(o.getClass().getName(), o);
+ }
+ }
+
+ public static class DynamicFilterRegistration implements Dynamic {
+ FilterConfigImpl fcfg;
+
+ public DynamicFilterRegistration(
+ org.apache.tomcat.lite.servlet.FilterConfigImpl fc) {
+ }
+
+ @Override
+ public void addMappingForServletNames(EnumSet<DispatcherType> dispatcherTypes,
+ boolean isMatchAfter,
+ String... servletNames) {
+ if (fcfg.ctx.startDone) {
+ // Use the context method instead of the servlet API to
+ // add mappings after context init.
+ throw new IllegalStateException();
+ }
+ ArrayList<String> dispatchers = new ArrayList<String>();
+ for (DispatcherType dt: dispatcherTypes) {
+ dispatchers.add(dt.name());
+ }
+ for (String servletName: servletNames) {
+ fcfg.ctx.getFilterMapper().addMapping(fcfg.getFilterName(),
+ null, servletName, (String[]) dispatchers.toArray(), isMatchAfter);
+ }
+ }
+
+ @Override
+ public void addMappingForUrlPatterns(EnumSet<DispatcherType> dispatcherTypes,
+ boolean isMatchAfter,
+ String... urlPatterns) {
+ if (fcfg.ctx.startDone) {
+ // Use the context method instead of the servlet API to
+ // add mappings after context init.
+ throw new IllegalStateException();
+ }
+ ArrayList<String> dispatchers = new ArrayList<String>();
+ for (DispatcherType dt: dispatcherTypes) {
+ dispatchers.add(dt.name());
+ }
+ for (String url: urlPatterns) {
+ fcfg.ctx.getFilterMapper().addMapping(fcfg.getFilterName(),
+ url, null, (String[]) dispatchers.toArray(), isMatchAfter);
+ }
+ }
+
+ @Override
+ public boolean setInitParameter(String name, String value)
+ throws IllegalArgumentException, IllegalStateException {
+ return fcfg.ctx.setInitParameter(fcfg.ctx, fcfg.initParams,
+ name, value);
+ }
+
+ @Override
+ public Set<String> setInitParameters(Map<String, String> initParameters)
+ throws IllegalArgumentException, IllegalStateException {
+ return ServletContextImpl.setInitParameters(fcfg.ctx, fcfg.initParams,
+ initParameters);
+ }
+
+ @Override
+ public void setAsyncSupported(boolean isAsyncSupported)
+ throws IllegalStateException {
+ fcfg.asyncSupported = isAsyncSupported;
+ }
+
+ @Override
+ public Collection<String> getServletNameMappings() {
+ return null;
+ }
+
+ @Override
+ public Collection<String> getUrlPatternMappings() {
+ return null;
+ }
+
+ @Override
+ public String getClassName() {
+ return null;
+ }
+
+ @Override
+ public String getInitParameter(String name) {
+ return null;
+ }
+
+ @Override
+ public Map<String, String> getInitParameters() {
+ return null;
+ }
+
+ @Override
+ public String getName() {
+ return null;
+ }
+ }
+
+ class ServletDynamicRegistration implements Dynamic {
+ ServletConfigImpl sc;
+
+ @Override
+ public void setAsyncSupported(boolean isAsyncSupported)
+ throws IllegalStateException {
+ sc.asyncSupported = isAsyncSupported;
+ }
+
+ @Override
+ public boolean setInitParameter(String name, String value)
+ throws IllegalArgumentException, IllegalStateException {
+ return sc.setInitParameter(name, value);
+ }
+
+ @Override
+ public Set<String> setInitParameters(Map<String, String> initParameters)
+ throws IllegalArgumentException, IllegalStateException {
+ return setInitParameters(initParameters);
+ }
+
+ @Override
+ public void addMappingForServletNames(
+ EnumSet<DispatcherType> dispatcherTypes, boolean isMatchAfter,
+ String... servletNames) {
+ }
+
+ @Override
+ public void addMappingForUrlPatterns(
+ EnumSet<DispatcherType> dispatcherTypes, boolean isMatchAfter,
+ String... urlPatterns) {
+ }
+
+ @Override
+ public Collection<String> getServletNameMappings() {
+ return null;
+ }
+
+ @Override
+ public Collection<String> getUrlPatternMappings() {
+ return null;
+ }
+
+ @Override
+ public String getClassName() {
+ return null;
+ }
+
+ @Override
+ public String getInitParameter(String name) {
+ return null;
+ }
+
+ @Override
+ public Map<String, String> getInitParameters() {
+ return null;
+ }
+
+ @Override
+ public String getName() {
+ return null;
+ }
+
+ }
+
+}
--- /dev/null
+/*
+ * Copyright 1999-2002,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.servlet;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.lang.reflect.Method;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.SingleThreadModel;
+import javax.servlet.UnavailableException;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.tomcat.lite.http.HttpChannel;
+import org.apache.tomcat.lite.http.HttpRequest;
+import org.apache.tomcat.lite.http.HttpResponse;
+import org.apache.tomcat.lite.http.MappingData;
+import org.apache.tomcat.servlets.jsp.BaseJspLoader;
+import org.apache.tomcat.servlets.util.Enumerator;
+
+/**
+ * Based on Wrapper.
+ *
+ * Standard implementation of the <b>Wrapper</b> interface that represents
+ * an individual servlet definition. No child Containers are allowed, and
+ * the parent Container must be a Context.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ */
+@SuppressWarnings("deprecation")
+public class ServletConfigImpl implements ServletConfig, HttpChannel.HttpService {
+
+ protected boolean asyncSupported;
+
+ private static Logger log=
+ Logger.getLogger(ServletConfigImpl.class.getName());
+
+ private static final String[] DEFAULT_SERVLET_METHODS = new String[] {
+ "GET", "HEAD", "POST" };
+
+ // TODO: refactor all 'stm' to separate class (not implemented)
+ // public static final String SINGLE_THREADED_PROXY =
+ // "org.apache.tomcat.servlets.jsp.SingleThreadedProxyServlet";
+
+ protected String description;
+ protected Map<String, String> initParams = new HashMap<String, String>();
+ protected String servletName;
+ protected String servletClassName;
+ protected String jspFile;
+ protected int loadOnStartup = -1;
+ protected String runAs;
+
+ protected Map securityRoleRef = new HashMap(); // roleName -> [roleLink]
+
+ /**
+ * The date and time at which this servlet will become available (in
+ * milliseconds since the epoch), or zero if the servlet is available.
+ * If this value equals Long.MAX_VALUE, the unavailability of this
+ * servlet is considered permanent.
+ */
+ private transient long available = 0L;
+
+ private ServletContextImpl ctx;
+
+ /**
+ * The (single) initialized instance of this servlet.
+ */
+ private transient Servlet instance = null;
+
+ /**
+ * Are we unloading our servlet instance at the moment?
+ */
+ private transient boolean unloading = false;
+
+ private Class servletClass = null;
+
+ // Support for SingleThreaded
+ /**
+ * The count of allocations that are currently active (even if they
+ * are for the same instance, as will be true on a non-STM servlet).
+ */
+ private transient int countAllocated = 0;
+
+ private transient boolean singleThreadModel = false;
+ /**
+ * Stack containing the STM instances.
+ */
+ private transient Stack instancePool = null;
+
+
+ // Statistics
+ private transient long loadTime=0;
+ private transient int classLoadTime=0;
+
+ public AtomicLong processingTime = new AtomicLong();
+ public AtomicInteger maxTime = new AtomicInteger();
+ public AtomicInteger requestCount = new AtomicInteger();
+ public AtomicInteger errorCount = new AtomicInteger();
+
+ // ------------------------------------------------------------- Properties
+ public ServletConfigImpl(ServletContextImpl ctx, String name,
+ String classname) {
+ this.servletName = name;
+ this.servletClassName = classname;
+ this.ctx = ctx;
+ ctx.lite.notifyAdd(this);
+ }
+
+ /**
+ * Return the available date/time for this servlet, in milliseconds since
+ * the epoch. If this date/time is Long.MAX_VALUE, it is considered to mean
+ * that unavailability is permanent and any request for this servlet will return
+ * an SC_NOT_FOUND error. If this date/time is in the future, any request for
+ * this servlet will return an SC_SERVICE_UNAVAILABLE error. If it is zero,
+ * the servlet is currently available.
+ */
+ public long getAvailable() {
+ return (this.available);
+ }
+
+ /**
+ * Set the available date/time for this servlet, in milliseconds since the
+ * epoch. If this date/time is Long.MAX_VALUE, it is considered to mean
+ * that unavailability is permanent and any request for this servlet will return
+ * an SC_NOT_FOUND error. If this date/time is in the future, any request for
+ * this servlet will return an SC_SERVICE_UNAVAILABLE error.
+ *
+ * @param available The new available date/time
+ */
+ public void setAvailable(long available) {
+
+ long oldAvailable = this.available;
+ if (available > System.currentTimeMillis())
+ this.available = available;
+ else
+ this.available = 0L;
+
+ }
+
+
+ /**
+ * Return the number of active allocations of this servlet, even if they
+ * are all for the same instance (as will be true for servlets that do
+ * not implement <code>SingleThreadModel</code>.
+ */
+ public int getCountAllocated() {
+ return (this.countAllocated);
+ }
+
+ /**
+ * Return the jsp-file setting for this servlet.
+ */
+ public String getJspFile() {
+ return jspFile;
+ }
+
+ public void setJspFile(String s) {
+ this.jspFile = s;
+ }
+
+ /**
+ * Return the load-on-startup order value (negative value means
+ * load on first call).
+ */
+ public int getLoadOnStartup() {
+ return loadOnStartup;
+ }
+
+ /**
+ * Return the fully qualified servlet class name for this servlet.
+ */
+ public String getServletClass() {
+ return servletClassName;
+ }
+
+ /**
+ * Is this servlet currently unavailable?
+ */
+ public boolean isUnavailable() {
+ if (available == 0L)
+ return (false);
+ else if (available <= System.currentTimeMillis()) {
+ available = 0L;
+ return (false);
+ } else
+ return (true);
+
+ }
+
+
+ /**
+ * Gets the names of the methods supported by the underlying servlet.
+ *
+ * This is the same set of methods included in the Allow response header
+ * in response to an OPTIONS request method processed by the underlying
+ * servlet.
+ *
+ * @return Array of names of the methods supported by the underlying
+ * servlet
+ * @throws IOException
+ */
+ public String[] getServletMethods() throws ServletException, IOException {
+
+ Class servletClazz = loadServlet().getClass();
+ if (!javax.servlet.http.HttpServlet.class.isAssignableFrom(
+ servletClazz)) {
+ return DEFAULT_SERVLET_METHODS;
+ }
+
+ HashSet allow = new HashSet();
+ allow.add("TRACE");
+ allow.add("OPTIONS");
+
+ Method[] methods = getAllDeclaredMethods(servletClazz);
+ for (int i=0; methods != null && i<methods.length; i++) {
+ Method m = methods[i];
+
+ if (m.getName().equals("doGet")) {
+ allow.add("GET");
+ allow.add("HEAD");
+ } else if (m.getName().equals("doPost")) {
+ allow.add("POST");
+ } else if (m.getName().equals("doPut")) {
+ allow.add("PUT");
+ } else if (m.getName().equals("doDelete")) {
+ allow.add("DELETE");
+ }
+ }
+
+ String[] methodNames = new String[allow.size()];
+ return (String[]) allow.toArray(methodNames);
+
+ }
+
+
+ // --------------------------------------------------------- Public Methods
+
+
+ /**
+ * MUST be called before service()
+ * This method should be called to get the servlet. After
+ * service(), dealocate should be called. This deals with STM and
+ * update use counters.
+ *
+ * Normally called from RequestDispatcher and TomcatLite.
+ */
+ public Servlet allocate() throws ServletException {
+ // If we are currently unloading this servlet, throw an exception
+ if (unloading)
+ throw new ServletException
+ ("allocate() while unloading " + getServletName());
+
+ Servlet servlet = null;
+ // never loaded.
+ synchronized (this) {
+ if (instance == null && !singleThreadModel) {
+ try {
+ servlet = loadServlet();
+ } catch (ServletException e) {
+ throw e;
+ } catch (Throwable e) {
+ throw new ServletException("loadServlet()", e);
+ }
+
+ if (servlet != null && !singleThreadModel) {
+ setServlet(servlet);
+ }
+ }
+ }
+
+
+ // If not SingleThreadedModel, return the same instance every time
+ if (instance != null) {
+ countAllocated++;
+ return (instance);
+ }
+
+ // Simpler policy for ST: unbound number of servlets ( can grow to
+ // one per thread )
+
+ synchronized (instancePool) {
+ if (instancePool.isEmpty()) {
+ try {
+ if (servlet != null) {
+ // this is the first invocation
+ countAllocated++;
+ return servlet;
+ }
+ countAllocated++;
+ Servlet newServlet = loadServlet();
+ log.fine("New STM servet " + newServlet + " " +
+ countAllocated);
+ return newServlet;
+ } catch (ServletException e) {
+ throw e;
+ } catch (Throwable e) {
+ throw new ServletException("allocate " + getServletName(),
+ e);
+ }
+ }
+ log.fine("Get from pool " + instancePool.size() + " " +
+ countAllocated);
+ Servlet s = (Servlet) instancePool.pop();
+ countAllocated++;
+ log.fine("After get " + instancePool.size() + " " + s +
+ " " + countAllocated);
+ return s;
+ }
+ }
+
+
+ /**
+ * MUST be called after service().
+ */
+ public void deallocate(Servlet servlet) {
+ // If not SingleThreadModel, no action is required
+ if (!singleThreadModel) {
+ countAllocated--;
+ return;
+ }
+
+ // Unlock and free this instance
+ synchronized (instancePool) {
+ countAllocated--;
+ if (instancePool.contains(servlet)) {
+ System.err.println("Aleady in pool " + servlet + " "
+ + instancePool.size()+ " " + countAllocated);
+ return;
+ }
+ System.err.println("return pool " + servlet + " " +
+ instancePool.size() + " " + countAllocated);
+ instancePool.push(servlet);
+ }
+ }
+
+ public Servlet newInstance() throws ServletException, IOException {
+ String actualClass = servletClassName;
+
+ if (instance != null) {
+ return instance;
+ }
+ if (actualClass == null) {
+ // No explicit name. Try to use the framework
+ if (jspFile != null) {
+ BaseJspLoader mapper = new JspLoader();
+ return mapper.loadProxy(jspFile, ctx, this);
+ }
+ if (actualClass == null) {
+ // Object manager can manage servlets.
+ Servlet res = (Servlet) ctx.getObjectManager().get( servletName +
+ "-servlet");
+ if (res != null) {
+ servletClass = res.getClass();
+ actualClass = servletClass.getName();
+ return res;
+ }
+ }
+
+ //ctx.getObjectManager().getObject(c);
+ //ctx.getObjectManager().getObject(servletName);
+ }
+
+
+ if (servletClass == null) {
+ // set classClass
+ loadClass(actualClass);
+ }
+
+
+ // jsp-file case. Load the JspProxyServlet instead, with the
+ // right params. Note the JspProxyServlet is _not_ jasper,
+ // nor 'jsp' servlet - it is just a proxy with no special
+ // params. It calls the jsp servlet and jasper to generate the
+ // real class.
+
+ // this is quite different from catalina, where an ugly kludge was
+ // used to use the same jsp servlet in 2 roles
+
+ // the jsp proxy is replaced by the web.xml processor
+
+ if (servletClass == null) {
+ unavailable(null);
+ throw new UnavailableException("ClassNotFound: " + actualClass);
+ }
+
+ // Instantiate and initialize an instance of the servlet class itself
+ try {
+ return (Servlet) servletClass.newInstance();
+ } catch (ClassCastException e) {
+ unavailable(null);
+ throw new UnavailableException("ClassCast: (Servlet)" +
+ actualClass);
+ } catch (Throwable e) {
+ unavailable(null);
+
+ // Added extra log statement for Bugzilla 36630:
+ // http://issues.apache.org/bugzilla/show_bug.cgi?id=36630
+ if(log.isLoggable(Level.FINE)) {
+ log.log(Level.FINE, "newInstance() error: servlet-name: " +
+ getServletName() +
+ " servlet-class: " + actualClass, e);
+ }
+
+ // Restore the context ClassLoader
+ throw new ServletException("newInstance() error " + getServletName() +
+ " " + actualClass, e);
+ }
+ }
+
+ /**
+ * Load and initialize an instance of this servlet, if there is not already
+ * at least one initialized instance. This can be used, for example, to
+ * load servlets that are marked in the deployment descriptor to be loaded
+ * at server startup time.
+ * @throws IOException
+ */
+ public synchronized Servlet loadServlet() throws ServletException, IOException {
+ // Nothing to do if we already have an instance or an instance pool
+ if (!singleThreadModel && (instance != null))
+ return instance;
+
+ long t1=System.currentTimeMillis();
+
+ Servlet servlet = newInstance();
+
+ classLoadTime=(int) (System.currentTimeMillis() -t1);
+
+ // Call the initialization method of this servlet
+ try {
+ servlet.init(this);
+ } catch (UnavailableException f) {
+ unavailable(f);
+ throw f;
+ } catch (ServletException f) {
+ throw f;
+ } catch (Throwable f) {
+ getServletContext().log("StandardWrapper.Throwable", f );
+ throw new ServletException("Servlet.init()", f);
+ }
+
+ // Register our newly initialized instance
+ singleThreadModel = servlet instanceof SingleThreadModel;
+ if (singleThreadModel) {
+ if (instancePool == null)
+ instancePool = new Stack();
+ }
+ loadTime=System.currentTimeMillis() -t1;
+
+ return servlet;
+ }
+
+
+ private void loadClass(String actualClass) throws ServletException {
+ // Complain if no servlet class has been specified
+ if (actualClass == null) {
+ unavailable(null);
+ throw new ServletException("servlet-class missing " +
+ getServletName());
+ }
+
+ ClassLoader classLoader = ctx.getClassLoader();
+ if (classLoader == null )
+ classLoader = this.getClass().getClassLoader();
+
+ // Load the specified servlet class from the appropriate class loader
+ try {
+ servletClass = classLoader.loadClass(actualClass);
+ } catch (ClassNotFoundException e) {
+ servletClass = null;
+ }
+ }
+
+ /**
+ * Return a String representation of this component.
+ */
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ if (ctx != null) {
+ sb.append(ctx.toString());
+ sb.append(".");
+ }
+ sb.append("Servlet[");
+ sb.append(getServletName()).append(" ");
+ sb.append(servletClassName);
+ if (jspFile != null) {
+ sb.append(" jsp=").append(jspFile);
+ }
+ sb.append("]");
+ return (sb.toString());
+ }
+
+
+ /**
+ * Process an UnavailableException, marking this servlet as unavailable
+ * for the specified amount of time.
+ *
+ * @param unavailable The exception that occurred, or <code>null</code>
+ * to mark this servlet as permanently unavailable
+ */
+ public void unavailable(UnavailableException unavailable) {
+ getServletContext().log("UnavailableException:" + getServletName());
+ if (unavailable == null)
+ setAvailable(Long.MAX_VALUE);
+ else if (unavailable.isPermanent())
+ setAvailable(Long.MAX_VALUE);
+ else {
+ int unavailableSeconds = unavailable.getUnavailableSeconds();
+ if (unavailableSeconds <= 0)
+ unavailableSeconds = 60; // Arbitrary default
+ setAvailable(System.currentTimeMillis() +
+ (unavailableSeconds * 1000L));
+ }
+
+ }
+
+
+ /**
+ * Unload all initialized instances of this servlet, after calling the
+ * <code>destroy()</code> method for each instance. This can be used,
+ * for example, prior to shutting down the entire servlet engine, or
+ * prior to reloading all of the classes from the Loader associated with
+ * our Loader's repository.
+ *
+ * @exception ServletException if an exception is thrown by the
+ * destroy() method
+ */
+ public synchronized void unload() throws ServletException {
+ setAvailable(Long.MAX_VALUE);
+
+ // Nothing to do if we have never loaded the instance
+ if (!singleThreadModel && (instance == null))
+ return;
+ unloading = true;
+
+ // Loaf a while if the current instance is allocated
+ // (possibly more than once if non-STM)
+ if (countAllocated > 0) {
+ int nRetries = 0;
+ long delay = ctx.getUnloadDelay() / 20;
+ while ((nRetries < 21) && (countAllocated > 0)) {
+ if ((nRetries % 10) == 0) {
+ log.info("Servlet.unload() timeout " +
+ countAllocated);
+ }
+ try {
+ Thread.sleep(delay);
+ } catch (InterruptedException e) {
+ ;
+ }
+ nRetries++;
+ }
+ }
+
+ ClassLoader oldCtxClassLoader =
+ Thread.currentThread().getContextClassLoader();
+ if (instance != null) {
+ ClassLoader classLoader = instance.getClass().getClassLoader();
+
+ PrintStream out = System.out;
+ // Call the servlet destroy() method
+ try {
+ Thread.currentThread().setContextClassLoader(classLoader);
+ instance.destroy();
+ } catch (Throwable t) {
+ instance = null;
+ //instancePool = null;
+ unloading = false;
+ throw new ServletException("Servlet.destroy() " +
+ getServletName(), t);
+ } finally {
+ // restore the context ClassLoader
+ Thread.currentThread().setContextClassLoader(oldCtxClassLoader);
+ }
+
+ // Deregister the destroyed instance
+ instance = null;
+ }
+ if (singleThreadModel && (instancePool != null)) {
+ try {
+ ClassLoader classLoader = ctx.getClassLoader();
+ Thread.currentThread().setContextClassLoader(classLoader);
+ while (!instancePool.isEmpty()) {
+ ((Servlet) instancePool.pop()).destroy();
+ }
+ } catch (Throwable t) {
+ instancePool = null;
+ unloading = false;
+ throw new ServletException("Servlet.destroy() " + getServletName(), t);
+ } finally {
+ // restore the context ClassLoader
+ Thread.currentThread().setContextClassLoader
+ (oldCtxClassLoader);
+ }
+ instancePool = null;
+ }
+
+ singleThreadModel = false;
+
+ unloading = false;
+ }
+
+
+ /**
+ * Return the initialization parameter value for the specified name,
+ * if any; otherwise return <code>null</code>.
+ *
+ * @param name Name of the initialization parameter to retrieve
+ */
+ public String getInitParameter(String name) {
+ return initParams.get(name);
+ }
+
+
+ /**
+ * Return the set of initialization parameter names defined for this
+ * servlet. If none are defined, an empty Enumeration is returned.
+ */
+ public Enumeration getInitParameterNames() {
+ synchronized (initParams) {
+ return (new Enumerator(initParams.keySet()));
+ }
+ }
+
+
+ /**
+ * Return the servlet context with which this servlet is associated.
+ */
+ public ServletContext getServletContext() {
+ return ctx;
+ }
+
+
+ /**
+ * Return the name of this servlet.
+ */
+ public String getServletName() {
+ return servletName;
+ }
+
+ private Method[] getAllDeclaredMethods(Class c) {
+
+ if (c.equals(javax.servlet.http.HttpServlet.class)) {
+ return null;
+ }
+
+ Method[] parentMethods = getAllDeclaredMethods(c.getSuperclass());
+
+ Method[] thisMethods = c.getDeclaredMethods();
+ if (thisMethods == null) {
+ return parentMethods;
+ }
+
+ if ((parentMethods != null) && (parentMethods.length > 0)) {
+ Method[] allMethods =
+ new Method[parentMethods.length + thisMethods.length];
+ System.arraycopy(parentMethods, 0, allMethods, 0,
+ parentMethods.length);
+ System.arraycopy(thisMethods, 0, allMethods, parentMethods.length,
+ thisMethods.length);
+
+ thisMethods = allMethods;
+ }
+
+ return thisMethods;
+ }
+
+ /** Specify the instance. Avoids the class lookup, disables unloading.
+ * Use for embedded case, or to control the allocation.
+ *
+ * @param servlet
+ */
+ public void setServlet(Servlet servlet) {
+ instance = servlet;
+ ctx.getObjectManager().bind("Servlet:" +
+ ctx.getContextPath() + ":" + getServletName(),
+ this);
+ }
+
+ public String getSecurityRoleRef(String role) {
+ return (String)securityRoleRef.get(role);
+ }
+
+ public void setSecurityRoleRef(Map securityRoles) {
+ this.securityRoleRef = securityRoles;
+ }
+
+ public void setConfig(Map initParams) {
+ this.initParams = initParams;
+ }
+
+ public void setLoadOnStartup(int loadOnStartup) {
+ this.loadOnStartup = loadOnStartup;
+ }
+
+ public Set<String> addMapping(String... urlPatterns) {
+ if (ctx.startDone) {
+ // Use the context method instead of the servlet API to
+ // add mappings after context init.
+ throw new IllegalStateException();
+ }
+ Set<String> failed = new HashSet<String>();
+ for (String url: urlPatterns) {
+ if (url == null) {
+ throw new IllegalArgumentException();
+ }
+ if (ctx.contextConfig.servletMapping.get(url) != null) {
+ failed.add(url);
+ } else {
+ ctx.contextConfig.servletMapping.put(url, getServletName());
+ ctx.addMapping(url, this);
+ }
+ }
+ return failed;
+ }
+
+ public boolean setInitParameter(String name, String value)
+ throws IllegalArgumentException, IllegalStateException {
+ return ServletContextImpl.setInitParameter(ctx, initParams,
+ name, value);
+ }
+
+ public Set<String> setInitParameters(Map<String, String> initParameters)
+ throws IllegalArgumentException, IllegalStateException {
+ return ServletContextImpl.setInitParameters(ctx, initParams,
+ initParameters);
+ }
+
+ public void setServletClass(Class<? extends Servlet> servletClass2) {
+ servletClass = servletClass2;
+ }
+
+ @Override
+ public void service(HttpRequest httpReq, HttpResponse httpRes)
+ throws IOException {
+
+ HttpChannel client = httpReq.getHttpChannel();
+ ServletRequestImpl req = TomcatLite.getFacade(client.getRequest());
+ ServletResponseImpl res = req.getResponse();
+
+ // TODO
+ }
+
+ /** Coyote / mapper adapter. Result of the mapper.
+ *
+ * This replaces the valve chain, the path is:
+ * 1. coyote calls mapper -> result Adapter
+ * 2. service is called. Additional filters are set on the wrapper.
+ * @param mapRes
+ */
+ void serviceServlet(ServletContextImpl ctx,
+ ServletRequestImpl req,
+ ServletResponseImpl res,
+ ServletConfigImpl servletConfig,
+ MappingData mapRes)
+ throws IOException {
+
+ requestCount.incrementAndGet();
+ Servlet servlet = null;
+ long t0 = System.currentTimeMillis();
+
+ try {
+ if (servletConfig.isUnavailable()) {
+ handleUnavailable(res, servletConfig);
+ return;
+ }
+ try {
+ servlet = servletConfig.allocate();
+ } catch(ServletException ex) {
+ handleUnavailable(res, servletConfig);
+ return;
+ }
+ WebappFilterMapper filterMap = ctx.getFilterMapper();
+ FilterChainImpl chain =
+ filterMap.createFilterChain(req, servletConfig, servlet);
+
+ try {
+ if (chain == null) {
+ if (servlet != null) {
+ servlet.service(req, res);
+ } else {
+ System.err.println("No servlet " + req.getRequestURI());
+ res.sendError(404);
+ }
+ } else {
+ chain.doFilter(req, res);
+ }
+ } catch(UnavailableException ex) {
+ servletConfig.unavailable(ex);
+ handleUnavailable(res, servletConfig);
+ return;
+ }
+
+ // servlet completed without exception. Check status
+ int status = res.getStatus();
+ if (status != 200 && !res.isCommitted()) {
+ String statusPage = ctx.findStatusPage(status);
+
+ if (statusPage != null) {
+ ctx.handleStatusPage(req, res, status, statusPage);
+ } else {
+ // Send a default message body.
+ // TODO: maybe other mechanism to customize default.
+ res.defaultStatusPage(status, res.getMessage());
+ }
+ }
+
+ } catch (Throwable t) {
+ errorCount.incrementAndGet();
+ ctx.handleError(req, res, t);
+ } finally {
+ int time = (int) (System.currentTimeMillis() - t0);
+ if (time > maxTime.get()) {
+ maxTime.set(time);
+ }
+ processingTime.addAndGet(time);
+ if (servlet != null) { // single-thread servlet
+ servletConfig.deallocate(servlet);
+ }
+ }
+ }
+
+ private void handleUnavailable(ServletResponseImpl response,
+ ServletConfigImpl servletConfig)
+ throws IOException {
+ long available = servletConfig.getAvailable();
+ if ((available > 0L) && (available < Long.MAX_VALUE))
+ response.setDateHeader("Retry-After", available);
+ // TODO: handle via error pages !
+ response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
+ "Service unavailable");
+ }
+
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.servlet;
+
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.EventListener;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.servlet.Filter;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextAttributeEvent;
+import javax.servlet.ServletContextAttributeListener;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.ServletException;
+
+import org.apache.tomcat.integration.ObjectManager;
+import org.apache.tomcat.lite.http.BaseMapper;
+import org.apache.tomcat.lite.io.FileConnectorJavaIo;
+import org.apache.tomcat.servlets.config.ConfigLoader;
+import org.apache.tomcat.servlets.config.ServletContextConfig;
+import org.apache.tomcat.servlets.config.ServletContextConfig.FilterData;
+import org.apache.tomcat.servlets.config.ServletContextConfig.FilterMappingData;
+import org.apache.tomcat.servlets.config.ServletContextConfig.ServletData;
+import org.apache.tomcat.servlets.session.UserSessionManager;
+import org.apache.tomcat.servlets.util.Enumerator;
+import org.apache.tomcat.servlets.util.MimeMap;
+import org.apache.tomcat.servlets.util.RequestUtil;
+import org.apache.tomcat.servlets.util.UrlUtils;
+
+
+/**
+ * Context - initialized from web.xml or using APIs.
+ *
+ * Initialization order:
+ *
+ * - add all listeners
+ * - add all filters
+ * - add all servlets
+ *
+ * - session parameters
+ * -
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision: 793797 $ $Date: 2009-07-13 22:38:02 -0700 (Mon, 13 Jul 2009) $
+ */
+
+public abstract class ServletContextImpl implements ServletContext {
+
+ /**
+ * Empty collection to serve as the basis for empty enumerations.
+ */
+ private static final ArrayList empty = new ArrayList();
+
+ private Logger log;
+
+ /**
+ * Base path - the directory root of the webapp
+ */
+ protected String basePath = null;
+
+ protected String contextPath;
+
+ // All config from web.xml and other sources
+ protected ServletContextConfig contextConfig = null;
+
+ // Includes the default values, will be merged with contextConfig
+ MimeMap contentTypes = new MimeMap();
+
+ /**
+ * The context attributes for this context.
+ */
+ protected transient Map<String, Object> attributes = new HashMap<String, Object>();
+
+ protected transient ArrayList<EventListener> lifecycleListeners =
+ new ArrayList<EventListener>();
+
+ protected UserSessionManager manager;
+
+ HashMap<String, FilterConfigImpl> filters = new HashMap<String, FilterConfigImpl>();
+
+ HashMap<String, ServletConfigImpl> servlets = new HashMap<String, ServletConfigImpl>();
+
+ /** Mapper for filters.
+ */
+ protected WebappFilterMapper webappFilterMapper;
+
+ /** Internal mapper for request dispatcher, must have all
+ * context mappings.
+ */
+ protected BaseMapper.ContextMapping mapper;
+
+ // From localeEncodingMapping
+ Locale2Charset charsetMapper = new Locale2Charset();
+
+ TomcatLite lite;
+
+ // Can use a separate injection config and framework
+ ObjectManager om;
+
+ private String hostname;
+
+ boolean initDone = false;
+
+ boolean startDone = false;
+
+ String defaultServlet = "org.apache.tomcat.servlets.file.WebdavServlet";
+ String jspWildcardServlet = "org.apache.tomcat.servlets.jsp.WildcardTemplateServlet";
+ String userSessionManager = "org.apache.tomcat.servlets.session.SimpleSessionManager";
+ String jspcServlet = "org.apache.tomcat.servlets.jspc.JspcServlet";
+
+ // ------------------------------------------------- ServletContext Methods
+ public ServletContextImpl() {
+ }
+
+ public void setTomcat(TomcatLite facade) {
+ this.lite = facade;
+ }
+
+ /**
+ * Registry/framework interface associated with the context.
+ * Also available as a context attribute.
+ * @return
+ */
+ public ObjectManager getObjectManager() {
+ if (om == null) {
+ om = lite.getObjectManager();
+ }
+ return om;
+ }
+
+ public void setObjectManager(ObjectManager om) {
+ this.om = om;
+ }
+
+ public Locale2Charset getCharsetMapper() {
+ return charsetMapper;
+ }
+
+ /**
+ * Set the context path, starting with "/" - "/" for ROOT
+ * @param path
+ */
+ public void setContextPath(String path) {
+ this.contextPath = path;
+ log = Logger.getLogger("webapp" + path.replace('/', '.'));
+ }
+
+ public void setHostname(String hostname) {
+ this.hostname = hostname;
+ }
+
+ public String getHostname() {
+ return hostname;
+ }
+
+ /** The directory where this app is based. May be null.
+ *
+ * @param basePath
+ */
+ public void setBasePath(String basePath) {
+ this.basePath = basePath;
+ }
+
+ public ServletContextConfig getContextConfig() {
+ return contextConfig;
+ }
+
+ /** The directory where this app is based.
+ *
+ * @param basePath
+ */
+ public String getBasePath() {
+ return basePath;
+ }
+
+ public String getEncodedPath() {
+ return null;
+ }
+
+
+ public boolean getCookies() {
+ return false;
+ }
+
+
+ public ServletContext getServletContext() {
+ return this;
+ }
+
+ public List<EventListener> getListeners() {
+ return lifecycleListeners;
+ }
+
+ public <T extends EventListener> void addListener(T listener) {
+ lifecycleListeners.add(listener);
+ }
+
+ public void removeListener(EventListener listener) {
+ lifecycleListeners.remove(listener);
+ }
+
+ public void addListener(Class<? extends EventListener> listenerClass) {
+ }
+
+ public void addListener(String className) {
+ }
+
+ public <T extends EventListener> T createListener(Class<T> c)
+ throws ServletException {
+ return null;
+ }
+
+
+ public void declareRoles(String... roleNames) {
+ }
+
+ public int getEffectiveMajorVersion() {
+ return 0;
+ }
+
+ public int getEffectiveMinorVersion() {
+ return 0;
+ }
+
+
+ public Logger getLogger() {
+ return log;
+ }
+
+ public long getUnloadDelay() {
+ return 0;
+ }
+
+ public ServletConfigImpl getServletConfig(String jsp_servlet_name) {
+ return (ServletConfigImpl)servlets.get(jsp_servlet_name);
+ }
+
+ public Map getServletConfigs() {
+ return servlets;
+ }
+
+ /**
+ * Add a servlet to the context.
+ * Called from processWebAppData()
+ *
+ * @param servletConfig
+ */
+ public void addServletConfig(ServletConfigImpl servletConfig) {
+ servlets.put(servletConfig.getServletName(), servletConfig);
+ }
+
+ public boolean getPrivileged() {
+ return false;
+ }
+
+
+ public Map getFilters() {
+ return filters;
+ }
+
+
+ protected boolean getCrossContext() {
+ return true;
+ }
+
+ public void addMimeType(String ext, String type) {
+ contentTypes.addContentType(ext, type);
+ }
+
+ public WebappFilterMapper getFilterMapper() {
+ if (webappFilterMapper == null) {
+ Object customMapper = getObjectManager().get(WebappFilterMapper.class);
+ if (customMapper == null) {
+ webappFilterMapper = new WebappFilterMapper();
+ } else {
+ webappFilterMapper = (WebappFilterMapper) customMapper;
+ }
+ webappFilterMapper.setServletContext(this);
+ }
+
+ return webappFilterMapper ;
+ }
+
+ public FilterConfigImpl getFilter(String name) {
+ return (FilterConfigImpl)filters.get(name);
+ }
+
+ /**
+ * Return the value of the specified context attribute, if any;
+ * otherwise return <code>null</code>.
+ *
+ * @param name Name of the context attribute to return
+ */
+ public Object getAttribute(String name) {
+ if ("ObjectManager".equals(name)) {
+ return getObjectManager();
+ }
+ if ("context-listeners".equals(name)) {
+ return lifecycleListeners;
+ }
+ return (attributes.get(name));
+ }
+
+ /**
+ * Return an enumeration of the names of the context attributes
+ * associated with this context.
+ */
+ public Enumeration getAttributeNames() {
+ return new Enumerator(attributes.keySet(), true);
+ }
+
+ /**
+ * Return a <code>ServletContext</code> object that corresponds to a
+ * specified URI on the server. This method allows servlets to gain
+ * access to the context for various parts of the server, and as needed
+ * obtain <code>RequestDispatcher</code> objects or resources from the
+ * context. The given path must be absolute (beginning with a "/"),
+ * and is interpreted based on our virtual host's document root.
+ *
+ * @param uri Absolute URI of a resource on the server
+ */
+ public ServletContext getContext(String uri) {
+ // TODO: support real uri ( http://host/path )
+ // Validate the format of the specified argument
+ if ((uri == null) || (!uri.startsWith("/")))
+ return (null);
+
+ ServletContextImpl child = null;
+ try {
+ child = lite.getContext(this, uri);
+ } catch (IOException e) {
+ } catch (ServletException e) {
+ }
+
+ if (child == null)
+ return (null);
+
+ if (this.getCrossContext()) {
+ // If crossContext is enabled, can always return the context
+ return child.getServletContext();
+ } else if (child == this) {
+ // Can still return the current context
+ return this.getServletContext();
+ } else {
+ // Nothing to return
+ return (null);
+ }
+ }
+
+
+ /**
+ * Return the main path associated with this context.
+ */
+ public String getContextPath() {
+ return contextPath;
+ }
+
+
+ /**
+ * Return the value of the specified initialization parameter, or
+ * <code>null</code> if this parameter does not exist.
+ *
+ * @param name Name of the initialization parameter to retrieve
+ */
+ public String getInitParameter(final String name) {
+ return ((String) contextConfig.contextParam.get(name));
+ }
+
+
+ /**
+ * Return the names of the context's initialization parameters, or an
+ * empty enumeration if the context has no initialization parameters.
+ */
+ public Enumeration getInitParameterNames() {
+ return (new Enumerator(contextConfig.contextParam.keySet()));
+ }
+
+ public void setContextParams(Map newParams) {
+ contextConfig.contextParam = (HashMap) newParams;
+ }
+
+ /**
+ * Return the major version of the Java Servlet API that we implement.
+ */
+ public int getMajorVersion() {
+ return 2;
+ }
+
+
+ /**
+ * Return the minor version of the Java Servlet API that we implement.
+ */
+ public int getMinorVersion() {
+ return 5;
+ }
+
+
+ /**
+ * Return the MIME type of the specified file, or <code>null</code> if
+ * the MIME type cannot be determined.
+ *
+ * @param file Filename for which to identify a MIME type
+ */
+ public String getMimeType(String file) {
+ return contentTypes.getMimeType(file);
+ }
+
+ /**
+ * Return the real path for a given virtual path, if possible; otherwise
+ * return <code>null</code>.
+ *
+ * @param path The path to the desired resource
+ */
+ public String getRealPath(String path) {
+ if (path == null) {
+ return null;
+ }
+
+ File file = new File(basePath, path);
+ return (file.getAbsolutePath());
+ }
+
+ /**
+ * Return a <code>RequestDispatcher</code> object that acts as a
+ * wrapper for the named servlet.
+ *
+ * @param name Name of the servlet for which a dispatcher is requested
+ */
+ public RequestDispatcher getNamedDispatcher(String name) {
+ if (name == null) return null;
+ ServletConfigImpl wrapper =
+ (ServletConfigImpl) this.getServletConfig(name);
+ if (wrapper == null) return null;
+
+ return new RequestDispatcherImpl(wrapper, name);
+ }
+
+
+ /**
+ * Return a <code>RequestDispatcher</code> instance that acts as a
+ * wrapper for the resource at the given path. The path must begin
+ * with a "/" and is interpreted as relative to the current context root.
+ *
+ * @param path The path to the desired resource.
+ */
+ public RequestDispatcher getRequestDispatcher(String path) {
+ if (path == null) return null;
+
+ if (!path.startsWith("/"))
+ throw new IllegalArgumentException(path);
+
+ path = UrlUtils.normalize(path);
+ if (path == null) return (null);
+
+
+ return new RequestDispatcherImpl(this, path);
+ }
+
+ public RequestDispatcher getRequestDispatcher(String path,
+ int type,
+ String dispatcherPath) {
+ RequestDispatcher dispatcher = getRequestDispatcher(path);
+ //((RequestDispatcherImpl)dispatcher);
+ return dispatcher;
+ }
+
+ ThreadLocal requestDispatcherStack = new ThreadLocal();
+
+ protected ClassLoader classLoader;
+
+ private String classPath;
+
+
+// protected RequestDispatcherImpl getRequestDispatcher() {
+// ArrayList/*<RequestDispatcherImpl>*/ list =
+// (ArrayList)requestDispatcherStack.get();
+// if (list == null) {
+// list = new ArrayList();
+// requestDispatcherStack.set(list);
+// }
+//
+//
+// return null;
+// }
+
+ public void resetDispatcherStack() {
+
+ }
+
+ /**
+ * Return the URL to the resource that is mapped to a specified path.
+ * The path must begin with a "/" and is interpreted as relative to the
+ * current context root.
+ *
+ * @param path The path to the desired resource
+ *
+ * @exception MalformedURLException if the path is not given
+ * in the correct form
+ */
+ public URL getResource(String path)
+ throws MalformedURLException {
+
+ if (path == null || !path.startsWith("/")) {
+ throw new MalformedURLException("getResource() " + path);
+ }
+
+ path = UrlUtils.normalize(path);
+ if (path == null)
+ return (null);
+
+ String libPath = "/WEB-INF/lib/";
+ if ((path.startsWith(libPath)) && (path.endsWith(".jar"))) {
+ File jarFile = null;
+ jarFile = new File(basePath, path);
+ if (jarFile.exists()) {
+ return jarFile.toURL();
+ } else {
+ return null;
+ }
+ } else {
+ File resFile = new File(basePath + path);
+ if (resFile.exists()) {
+ return resFile.toURL();
+ }
+ }
+
+ return (null);
+
+ }
+
+ /**
+ * Return the requested resource as an <code>InputStream</code>. The
+ * path must be specified according to the rules described under
+ * <code>getResource</code>. If no such resource can be identified,
+ * return <code>null</code>.
+ *
+ * @param path The path to the desired resource.
+ */
+ public InputStream getResourceAsStream(String path) {
+
+ path = UrlUtils.normalize(path);
+ if (path == null)
+ return (null);
+
+ File resFile = new File(basePath + path);
+ if (!resFile.exists())
+ return null;
+
+ try {
+ return new FileInputStream(resFile);
+ } catch (FileNotFoundException e) {
+ return null;
+ }
+
+ }
+
+
+ /**
+ * Return a Set containing the resource paths of resources member of the
+ * specified collection. Each path will be a String starting with
+ * a "/" character. The returned set is immutable.
+ *
+ * @param path Collection path
+ */
+ public Set getResourcePaths(String path) {
+
+ // Validate the path argument
+ if (path == null) {
+ return null;
+ }
+ if (!path.startsWith("/")) {
+ throw new IllegalArgumentException("getResourcePaths() " + path);
+ }
+
+ path = UrlUtils.normalize(path);
+ if (path == null)
+ return (null);
+
+ File f = new File(basePath + path);
+ File[] files = f.listFiles();
+ if (files == null) return null;
+ if (!path.endsWith("/")) {
+ path = path + "/";
+ }
+
+ HashSet result = new HashSet();
+ for (int i=0; i < files.length; i++) {
+ if (files[i].isDirectory() ) {
+ result.add(path + files[i].getName() + "/");
+ } else {
+ result.add(path + files[i].getName());
+ }
+ }
+ return result;
+ }
+
+
+
+ /**
+ * Return the name and version of the servlet container.
+ */
+ public String getServerInfo() {
+ return "Apache Tomcat Lite";
+ }
+
+ /**
+ * @deprecated As of Java Servlet API 2.1, with no direct replacement.
+ */
+ public Servlet getServlet(String name) {
+ return (null);
+ }
+
+
+ /**
+ * Return the display name of this web application.
+ */
+ public String getServletContextName() {
+ return contextConfig.displayName;
+ }
+
+
+ /**
+ * @deprecated As of Java Servlet API 2.1, with no direct replacement.
+ */
+ public Enumeration getServletNames() {
+ return (new Enumerator(empty));
+ }
+
+
+ /**
+ * @deprecated As of Java Servlet API 2.1, with no direct replacement.
+ */
+ public Enumeration getServlets() {
+ return (new Enumerator(empty));
+ }
+
+
+ /**
+ * Writes the specified message to a servlet log file.
+ *
+ * @param message Message to be written
+ */
+ public void log(String message) {
+ this.getLogger().info(message);
+ }
+
+
+ /**
+ * Writes the specified exception and message to a servlet log file.
+ *
+ * @param exception Exception to be reported
+ * @param message Message to be written
+ *
+ * @deprecated As of Java Servlet API 2.1, use
+ * <code>log(String, Throwable)</code> instead
+ */
+ public void log(Exception exception, String message) {
+ this.getLogger().log(Level.INFO, message, exception);
+ }
+
+
+ /**
+ * Writes the specified message and exception to a servlet log file.
+ *
+ * @param message Message to be written
+ * @param throwable Exception to be reported
+ */
+ public void log(String message, Throwable throwable) {
+ this.getLogger().log(Level.INFO, message, throwable);
+ }
+
+ /**
+ * Remove the context attribute with the specified name, if any.
+ *
+ * @param name Name of the context attribute to be removed
+ */
+ public void removeAttribute(String name) {
+
+ Object value = null;
+ boolean found = false;
+
+ // Remove the specified attribute
+ // Check for read only attribute
+ found = attributes.containsKey(name);
+ if (found) {
+ value = attributes.get(name);
+ attributes.remove(name);
+ } else {
+ return;
+ }
+
+ // Notify interested application event listeners
+ List listeners = this.getListeners();
+ if (listeners.size() == 0)
+ return;
+ ServletContextAttributeEvent event = null;
+ for (int i = 0; i < listeners.size(); i++) {
+ if (!(listeners.get(i) instanceof ServletContextAttributeListener))
+ continue;
+ ServletContextAttributeListener listener =
+ (ServletContextAttributeListener) listeners.get(i);
+ try {
+ if (event == null) {
+ event = new ServletContextAttributeEvent(this.getServletContext(),
+ name, value);
+
+ }
+ listener.attributeRemoved(event);
+ } catch (Throwable t) {
+ // FIXME - should we do anything besides log these?
+ log("ServletContextAttributeListener", t);
+ }
+ }
+ }
+
+
+ /**
+ * Bind the specified value with the specified context attribute name,
+ * replacing any existing value for that name.
+ *
+ * @param name Attribute name to be bound
+ * @param value New attribute value to be bound
+ */
+ public void setAttribute(String name, Object value) {
+ // Name cannot be null
+ if (name == null)
+ throw new IllegalArgumentException
+ ("name == null");
+
+ // Null value is the same as removeAttribute()
+ if (value == null) {
+ removeAttribute(name);
+ return;
+ }
+
+ Object oldValue = null;
+ boolean replaced = false;
+
+ // Add or replace the specified attribute
+ synchronized (attributes) {
+ // Check for read only attribute
+ oldValue = attributes.get(name);
+ if (oldValue != null)
+ replaced = true;
+ attributes.put(name, value);
+ }
+
+ // Notify interested application event listeners
+ List listeners = this.getListeners();
+ if (listeners.size() == 0)
+ return;
+ ServletContextAttributeEvent event = null;
+ for (int i = 0; i < listeners.size(); i++) {
+ if (!(listeners.get(i) instanceof ServletContextAttributeListener))
+ continue;
+ ServletContextAttributeListener listener =
+ (ServletContextAttributeListener) listeners.get(i);
+ try {
+ if (event == null) {
+ if (replaced)
+ event =
+ new ServletContextAttributeEvent(this.getServletContext(),
+ name, oldValue);
+ else
+ event =
+ new ServletContextAttributeEvent(this.getServletContext(),
+ name, value);
+
+ }
+ if (replaced) {
+ listener.attributeReplaced(event);
+ } else {
+ listener.attributeAdded(event);
+ }
+ } catch (Throwable t) {
+ // FIXME - should we do anything besides log these?
+ log("ServletContextAttributeListener error", t);
+ }
+ }
+
+ }
+
+ /**
+ * Clear all application-created attributes.
+ */
+ void clearAttributes() {
+ // Create list of attributes to be removed
+ ArrayList list = new ArrayList();
+ synchronized (attributes) {
+ Iterator iter = attributes.keySet().iterator();
+ while (iter.hasNext()) {
+ list.add(iter.next());
+ }
+ }
+
+ // Remove application originated attributes
+ // (read only attributes will be left in place)
+ Iterator keys = list.iterator();
+ while (keys.hasNext()) {
+ String key = (String) keys.next();
+ removeAttribute(key);
+ }
+ }
+
+ public void initFilters() throws ServletException {
+ Iterator fI = getFilters().values().iterator();
+ while (fI.hasNext()) {
+ FilterConfigImpl fc = (FilterConfigImpl)fI.next();
+ try {
+ fc.getFilter(); // will triger init()
+ } catch (Throwable e) {
+ log.log(Level.WARNING, getContextPath() + " Filter.init() " +
+ fc.getFilterName(), e);
+ }
+
+ }
+ }
+
+ public void initServlets() throws ServletException {
+ Iterator fI = getServletConfigs().values().iterator();
+ Map/*<Integer, List<ServletConfigImpl>>*/ onStartup =
+ new TreeMap/*<Integer, List<ServletConfigImpl>>*/();
+ while (fI.hasNext()) {
+ ServletConfigImpl fc = (ServletConfigImpl)fI.next();
+ if (fc.getLoadOnStartup() > 0 ) {
+ Integer i = new Integer(fc.getLoadOnStartup());
+ List/*<ServletConfigImpl>*/ old = (List)onStartup.get(i);
+ if (old == null) {
+ old = new ArrayList/*<ServletConfigImpl>*/();
+ onStartup.put(i, old);
+ }
+ old.add(fc);
+ }
+ }
+ Iterator keys = onStartup.keySet().iterator();
+ while (keys.hasNext()) {
+ Integer key = (Integer)keys.next();
+ List/*<ServletConfigImpl>*/ servlets = (List)onStartup.get(key);
+ Iterator servletsI = servlets.iterator();
+ while (servletsI.hasNext()) {
+ ServletConfigImpl fc = (ServletConfigImpl) servletsI.next();
+ try {
+ fc.loadServlet();
+ } catch (Throwable e) {
+ log.log(Level.WARNING, "Error initializing " + fc.getServletName(), e);
+ }
+ }
+ }
+ }
+
+ public void initListeners() throws ServletException {
+ Iterator fI = contextConfig.listenerClass.iterator();
+ while (fI.hasNext()) {
+ String listenerClass = (String)fI.next();
+ try {
+ Object l = newInstance(listenerClass, "EventListener-" + listenerClass);
+ lifecycleListeners.add((EventListener) l);
+ } catch (Throwable e) {
+ log.log(Level.WARNING, "Error initializing listener " + listenerClass, e);
+ }
+ }
+ }
+
+ public Object newInstance(String className, String bindName) throws ServletException {
+ try {
+ Class cls = getClassLoader().loadClass(className);
+ Object l = cls.newInstance();
+
+ // Injections and JMX support
+ if (bindName != null) {
+ getObjectManager().bind("Context=" + getContextPath() + "," +
+ bindName, l);
+ }
+ return l;
+ } catch (Throwable e) {
+ log.log(Level.WARNING, "Error initializing listener " + className, e);
+ throw new ServletException(e);
+ }
+ }
+
+ public ClassLoader getClassLoader() {
+ return classLoader;
+ }
+
+ public void addMapping(String path, String name) {
+ ServletConfigImpl wrapper = getServletConfig(name);
+ addMapping(path, wrapper);
+ }
+
+ public void addMapping(String path, ServletConfigImpl wrapper) {
+ getEngine().getDispatcher().addWrapper(getContextMap(), path,
+ wrapper);
+ }
+
+
+
+ public void setWelcomeFiles(String[] name) {
+ getContextMap().welcomeResources = name;
+ }
+
+ public String[] getWelcomeFiles() {
+ return getContextMap().welcomeResources;
+ }
+
+ public BaseMapper.ContextMapping getContextMap() {
+ if (mapper == null) {
+ mapper = new BaseMapper.ContextMapping();
+ mapper.name = this.getContextPath();
+ mapper.welcomeResources = getWelcomeFiles();
+ }
+ return mapper;
+ }
+
+ public void setSessionTimeout(int to) {
+ getManager().setSessionTimeout(to);
+ }
+
+ /**
+ * Initialize the context from the parsed config.
+ *
+ * Note that WebAppData is serializable.
+ */
+ public void processWebAppData(ServletContextConfig d) throws ServletException {
+ this.contextConfig = d;
+
+ for (String k: d.mimeMapping.keySet()) {
+ addMimeType(k, d.mimeMapping.get(k));
+ }
+
+ String[] wFiles = (String[])d.welcomeFileList.toArray(new String[0]);
+ if (wFiles.length == 0) {
+ wFiles = new String[] {"index.html" };
+ }
+ if (basePath != null) {
+ // TODO: configurable filesystem
+ getContextMap().resources =
+ new FileConnectorJavaIo(new File(getBasePath()));
+ }
+ setWelcomeFiles(wFiles);
+
+ Iterator i2 = d.filters.values().iterator();
+ while (i2.hasNext()) {
+ FilterData fd = (FilterData)i2.next();
+ addFilter(fd.name, fd.className, fd.initParams);
+ }
+
+ Iterator i3 = d.servlets.values().iterator();
+ while (i3.hasNext()) {
+ ServletData sd = (ServletData) i3.next();
+ // jsp-file
+ if (sd.className == null) {
+ if (sd.jspFile == null) {
+ log.log(Level.WARNING, "Missing servlet class for " + sd.name);
+ continue;
+ }
+ }
+
+ ServletConfigImpl sw =
+ new ServletConfigImpl(this, sd.name, sd.className);
+ sw.setConfig(sd.initParams);
+ sw.setJspFile(sd.jspFile);
+ sw.setLoadOnStartup(sd.loadOnStartup);
+ //sw.setRunAs(sd.runAs);
+ sw.setSecurityRoleRef(sd.securityRoleRef);
+
+ addServletConfig(sw);
+ }
+
+ for (String k: d.servletMapping.keySet()) {
+ addMapping(k, d.servletMapping.get(k));
+ }
+
+ Iterator i5 = d.filterMappings.iterator();
+ while (i5.hasNext()) {
+ FilterMappingData k = (FilterMappingData) i5.next();
+ String[] disp = new String[k.dispatcher.size()];
+ if (k.urlPattern != null) {
+ addFilterMapping(k.urlPattern,
+ k.filterName,
+ (String[])k.dispatcher.toArray(disp));
+ }
+ if (k.servletName != null) {
+ addFilterServletMapping(k.servletName,
+ k.filterName,
+ (String[])k.dispatcher.toArray(disp));
+ }
+ }
+
+ for (String n: d.localeEncodingMapping.keySet()) {
+ getCharsetMapper().addCharsetMapping(n,
+ d.localeEncodingMapping.get(n));
+ }
+ }
+
+ public void addServlet(String servletName, String servletClass,
+ String jspFile, Map params) {
+ ServletConfigImpl sc = new ServletConfigImpl(this, servletName,
+ servletClass);
+ sc.setJspFile(jspFile);
+ sc.setConfig(params);
+ addServletConfig(sc);
+ }
+
+ public ServletConfigImpl add(String servletName, Servlet servlet) {
+ ServletConfigImpl sc = new ServletConfigImpl(this, servletName, null);
+ sc.setServlet(servlet);
+ addServletConfig(sc);
+ return sc;
+ }
+
+ public void addServletSec(String serlvetName, String runAs, Map roles) {
+ // TODO
+ }
+
+
+
+ public void addFilterMapping(String path, String filterName,
+ String[] dispatcher) {
+ getFilterMapper().addMapping(filterName,
+ path, null, dispatcher, true);
+
+ }
+
+ public void addFilterServletMapping(String servlet,
+ String filterName,
+ String[] dispatcher) {
+ getFilterMapper().addMapping(filterName,
+ null, servlet,
+ dispatcher, true);
+ }
+
+ /**
+ * Called from TomcatLite.init(), required before start.
+ *
+ * Will initialize defaults and load web.xml unless webAppData is
+ * already set and recent. No other processing is done except reading
+ * the config - you can add or alter it before start() is called.
+ *
+ * @throws ServletException
+ */
+ public ServletContextImpl loadConfig() throws ServletException {
+ long t0 = System.currentTimeMillis();
+ if (initDone) {
+ return this;
+ }
+ initDone = true;
+ // Load global init params from the facade
+ initEngineDefaults();
+
+ initTempDir();
+
+ if (getBasePath() == null || getBasePath().length() == 0) {
+ // dynamic context - no files or base path
+ contextConfig = new ServletContextConfig();
+ } else {
+ ConfigLoader cfgLoader = null;
+
+ initClassLoader(getBasePath());
+
+ if (lite.getDeployListener() != null) {
+ cfgLoader = (ConfigLoader) newInstance(lite.getDeployListener(), null);
+ } else {
+ cfgLoader = new ConfigLoader();
+ }
+
+ contextConfig = cfgLoader.loadConfig(getBasePath());
+ if (contextConfig == null) {
+ String msg = "No configuration found, run " +
+ "'java -jar WarDeploy.jar " + getBasePath() + "'";
+ System.err.println(msg);
+ throw new ServletException(msg);
+ }
+
+ processWebAppData(contextConfig);
+ }
+ // if not defined yet:
+ addDefaultServlets();
+
+ long t1 = System.currentTimeMillis();
+
+ // At this point all config is loaded. Contexts are not yet init()
+ // - this will happen on start.
+ log.fine("Context.loadConfig() " + contextPath + " " + (t1-t0));
+ return this;
+ }
+
+
+ protected void initTempDir() throws ServletException {
+ // We need a base path - at least for temp files, req. by spec
+ if (basePath == null) {
+ basePath = ("/".equals(contextPath)) ?
+ lite.getWork().getAbsolutePath() + "/ROOT" :
+ lite.getWork().getAbsolutePath() + contextPath;
+ }
+
+ File f = new File(basePath + "/WEB-INF/tmp");
+ f.mkdirs();
+ setAttribute("javax.servlet.context.tempdir", f);
+ }
+
+ /**
+ * Static file handler ( default )
+ * *.jsp support
+ *
+ */
+ protected void addDefaultServlets() throws ServletException {
+ if (servlets.get("default") == null) {
+ ServletConfigImpl fileS = new ServletConfigImpl(this,
+ "default", defaultServlet);
+ addServletConfig(fileS);
+ addMapping("/", fileS);
+ }
+
+ // *.jsp support
+ if (servlets.get("jspwildcard") == null) {
+ ServletConfigImpl fileS = new ServletConfigImpl(this,
+ "jspwildcard", jspWildcardServlet);
+ fileS.initParams.put("mapper", JspLoader.class.getName());
+ addServletConfig(fileS);
+ addMapping("*.jsp", fileS);
+ }
+
+ ServletConfigImpl jspcS = new ServletConfigImpl(this,
+ "jspc", jspcServlet);
+ addServletConfig(jspcS);
+ }
+
+ protected void initEngineDefaults() throws ServletException {
+
+ // TODO: make this customizable, avoid loading it on startup
+ // Set the class name as default in the addon support
+ for (String sname: lite.ctxDefaultInitParam.keySet()) {
+ String path = lite.ctxDefaultInitParam.get(sname);
+ contextConfig.contextParam.put(sname, path);
+ }
+
+ for (String sname: lite.preloadServlets.keySet()) {
+ String sclass = lite.preloadServlets.get(sname);
+ ServletConfigImpl fileS = new ServletConfigImpl(this, sname, sclass);
+ addServletConfig(fileS);
+ }
+
+ for (String sname: lite.preloadMappings.keySet()) {
+ String path = lite.preloadMappings.get(sname);
+ ServletConfigImpl servletConfig = getServletConfig(sname);
+ addMapping(path, servletConfig);
+ }
+ }
+
+
+ private void addClasspathLib(ArrayList res, File directory) {
+
+ if (!directory.isDirectory() || !directory.exists()
+ || !directory.canRead()) {
+ return;
+ }
+
+ File[] jars = directory.listFiles(new FilenameFilter() {
+ public boolean accept(File dir, String name) {
+ return name.endsWith(".jar");
+ }
+ });
+
+ for (int j = 0; j < jars.length; j++) {
+ try {
+ URL url = jars[j].toURL();
+ res.add(url);
+ } catch (MalformedURLException e) {
+ }
+ }
+ }
+
+ private void addClasspathDir(ArrayList res, File classesDir) {
+
+ if (classesDir.isDirectory() && classesDir.exists() &&
+ classesDir.canRead()) {
+ try {
+ URL url = classesDir.toURL();
+ res.add(url);
+ } catch (MalformedURLException e) {
+ }
+ }
+ }
+
+
+ public void start() throws ServletException {
+ if (startDone) {
+ return;
+ }
+ String base = getBasePath();
+
+ // JMX should know about us ( TODO: is it too early ? )
+ lite.notifyAdd(this);
+
+ initListeners();
+
+ List listeners = this.getListeners();
+ ServletContextEvent event = null;
+ for (int i = 0; i < listeners.size(); i++) {
+ if (!(listeners.get(i) instanceof ServletContextListener))
+ continue;
+ ServletContextListener listener =
+ (ServletContextListener) listeners.get(i);
+ if (event == null) {
+ event = new ServletContextEvent(this);
+ }
+ try {
+ // May add servlets/filters
+ listener.contextInitialized(event);
+ } catch (Throwable t) {
+ log.log(Level.WARNING, "Context.init() contextInitialized() error:", t);
+ }
+ }
+
+
+ initFilters();
+ initServlets();
+
+ startDone = true;
+ }
+
+ public String getClassPath() {
+ return classPath;
+ }
+
+ private void initClassLoader(String base) {
+ ArrayList urls = new ArrayList();
+
+ addClasspathDir(urls, new File(base + "/WEB-INF/classes"));
+ addClasspathDir(urls, new File(base + "/WEB-INF/tmp"));
+ addClasspathLib(urls, new File(base + "/WEB-INF/lib"));
+
+ URL[] urlsA = new URL[urls.size()];
+ urls.toArray(urlsA);
+ StringBuilder cp = new StringBuilder();
+
+ for (URL cpUrl : urlsA) {
+ cp.append(":").append(cpUrl.getFile());
+ }
+ classPath = cp.toString();
+ URLClassLoader parentLoader =
+ getEngine().getContextParentLoader();
+ // create a class loader.
+ // TODO: reimplement special 'deploy' dirs
+
+ /*
+ Repository ctxRepo = new Repository();
+ ctxRepo.setParentClassLoader(parentLoader);
+ ctxRepo.addURL(urlsA);
+ repository = ctxRepo;
+ */
+
+ classLoader = new URLClassLoader(urlsA, parentLoader);
+ }
+
+ public UserSessionManager getManager() {
+ if (manager == null) {
+ try {
+ manager = (UserSessionManager) getObjectManager().get(
+ UserSessionManager.class);
+ } catch (Throwable t) {
+ t.printStackTrace();
+ manager = null;
+ }
+ if (manager == null) {
+ try {
+ manager = (UserSessionManager) newInstance(userSessionManager, "UserSessionManager");
+ } catch (ServletException e) {
+ log.log(Level.SEVERE, "Error creating session manager", e);
+ return null;
+ }
+ }
+ manager.setContext(this);
+ if (contextConfig.sessionTimeout > 0 ) {
+ manager.setSessionTimeout(contextConfig.sessionTimeout);
+ }
+ }
+ return manager;
+ }
+
+
+ // TODO: configurable ? init-params
+ public String getSessionCookieName() {
+ return "JSESSIONID";
+ }
+
+
+
+ public void destroy() throws ServletException {
+ // destroy filters
+ Iterator fI = filters.values().iterator();
+ while(fI.hasNext()) {
+ FilterConfigImpl fc = (FilterConfigImpl) fI.next();
+ try {
+ fc.getFilter().destroy();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ // destroy servlets
+ fI = servlets.values().iterator();
+ while(fI.hasNext()) {
+ ServletConfigImpl fc = (ServletConfigImpl) fI.next();
+ try {
+ fc.unload();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public TomcatLite getEngine() {
+ return lite;
+ }
+
+ public String toString() {
+ return "{Context_path: " + getContextPath()
+ + ", dir=" + getBasePath() + "}";
+ }
+
+ public String findStatusPage(int status) {
+ if (contextConfig.errorPageCode.size() == 0) {
+ return null;
+ }
+ if (status == 200) {
+ return null;
+ }
+
+ return (String) contextConfig.errorPageCode.get(Integer.toString(status));
+ }
+
+ public void handleStatusPage(ServletRequestImpl req,
+ ServletResponseImpl res,
+ int status,
+ String statusPage) {
+ String message = RequestUtil.filter(res.getMessage());
+ if (message == null)
+ message = "";
+ setErrorAttributes(req, status, message);
+ dispatchError(req, res, statusPage);
+ }
+
+ protected void setErrorAttributes(ServletRequestImpl req,
+ int status,
+ String message) {
+ req.setAttribute("javax.servlet.error.status_code",
+ new Integer(status));
+ if (req.getWrapper() != null) {
+ req.setAttribute("javax.servlet.error.servlet_name",
+ req.getWrapper().servletName);
+ }
+ req.setAttribute("javax.servlet.error.request_uri",
+ req.getRequestURI());
+ req.setAttribute("javax.servlet.error.message",
+ message);
+
+ }
+
+ public void handleError(ServletRequestImpl req,
+ ServletResponseImpl res,
+ Throwable t) {
+ Throwable realError = t;
+ if (realError instanceof ServletException) {
+ realError = ((ServletException) realError).getRootCause();
+ if (realError == null) {
+ realError = t;
+ }
+ }
+ //if (realError instanceof ClientAbortException ) {
+
+ String errorPage = findErrorPage(t);
+ if ((errorPage == null) && (realError != t)) {
+ errorPage = findErrorPage(realError);
+ }
+
+ if (errorPage != null) {
+ setErrorAttributes(req, 500, t.getMessage());
+ req.setAttribute("javax.servlet.error.exception", realError);
+ req.setAttribute("javax.servlet.error.exception_type",
+ realError.getClass());
+ dispatchError(req, res, errorPage);
+ } else {
+ log("Unhandled error", t);
+ if (t instanceof ServletException &&
+ ((ServletException)t).getRootCause() != null) {
+ log("RootCause:", ((ServletException)t).getRootCause());
+ }
+ if (res.getStatus() < 500) {
+ res.setStatus(500);
+ }
+ }
+ }
+
+ protected void dispatchError(ServletRequestImpl req,
+ ServletResponseImpl res,
+ String errorPage) {
+ RequestDispatcher rd =
+ getRequestDispatcher(errorPage);
+ try {
+ // will clean up the buffer
+ rd.forward(req, res);
+ return; // handled
+ } catch (ServletException e) {
+ // TODO
+ } catch (IOException e) {
+ // TODO
+ }
+ }
+
+ protected String findErrorPage(Throwable exception) {
+ if (contextConfig.errorPageException.size() == 0) {
+ return null;
+ }
+ if (exception == null)
+ return (null);
+ Class clazz = exception.getClass();
+ String name = clazz.getName();
+ while (!Object.class.equals(clazz)) {
+ String page = (String)contextConfig.errorPageException.get(name);
+ if (page != null)
+ return (page);
+ clazz = clazz.getSuperclass();
+ if (clazz == null)
+ break;
+ name = clazz.getName();
+ }
+ return (null);
+
+ }
+
+ public void addFilter(String filterName, String filterClass,
+ Map params) {
+ FilterConfigImpl fc = new FilterConfigImpl(this);
+ fc.setData(filterName, filterClass, params);
+ filters.put(filterName, fc);
+ }
+
+ // That's tricky - this filter will have no name. We need to generate one
+ // because our code relies on names.
+ AtomicInteger autoName = new AtomicInteger();
+
+ public <T extends Filter> T createFilter(Class<T> c) throws ServletException {
+ FilterConfigImpl fc = new FilterConfigImpl(this);
+ String filterName = "_tomcat_auto_filter_" + autoName.incrementAndGet();
+ fc.setData(filterName, null, new HashMap());
+ fc.setFilterClass(c);
+ filters.put(filterName, fc);
+
+ try {
+ return (T) fc.createFilter();
+ } catch (ClassCastException e) {
+ throw new ServletException(e);
+ } catch (ClassNotFoundException e) {
+ throw new ServletException(e);
+ } catch (IllegalAccessException e) {
+ throw new ServletException(e);
+ } catch (InstantiationException e) {
+ throw new ServletException(e);
+ }
+ }
+
+ public <T extends Servlet> T createServlet(Class<T> c) throws ServletException {
+ String filterName = "_tomcat_auto_servlet_" + autoName.incrementAndGet();
+ ServletConfigImpl fc = new ServletConfigImpl(this, filterName, null);
+ fc.setServletClass(c);
+ servlets.put(filterName, fc);
+
+ try {
+ return (T) fc.newInstance();
+ } catch (ClassCastException e) {
+ throw new ServletException(e);
+ } catch (IOException e) {
+ throw new ServletException(e);
+ }
+ }
+
+ public boolean setInitParameter(String name, String value) {
+ HashMap<String, String> params = contextConfig.contextParam;
+ return setInitParameter(this, params, name, value);
+ }
+
+ static Set<String> setInitParameters(ServletContextImpl ctx,
+ Map<String, String> params,
+ Map<String, String> initParameters)
+ throws IllegalArgumentException, IllegalStateException {
+ if (ctx.startDone) {
+ throw new IllegalStateException();
+ }
+ Set<String> result = new HashSet<String>();
+ for (String name: initParameters.keySet()) {
+ String value = initParameters.get(name);
+ if (name == null || value == null) {
+ throw new IllegalArgumentException();
+ }
+ if (!setInitParameter(ctx, params, name, value)) {
+ result.add(name);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * true if the context initialization parameter with the given name and value was set successfully on this ServletContext, and false if it was not set because this ServletContext already contains a context initialization parameter with a matching name
+ * Throws:
+ * java.lang.IllegalStateException - if this ServletContext has already been initialized
+ */
+ static boolean setInitParameter(ServletContextImpl ctx, Map<String, String> params,
+ String name, String value) {
+ if (name == null || value == null) {
+ throw new IllegalArgumentException();
+ }
+ if (ctx.startDone) {
+ throw new IllegalStateException();
+ }
+ String oldValue = params.get(name);
+ if (oldValue != null) {
+ return false;
+ } else {
+ params.put(name, value);
+ return true;
+ }
+ }
+
+}
+
--- /dev/null
+/*
+ * 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.servlet;
+
+import java.io.IOException;
+
+import javax.servlet.ServletInputStream;
+
+import org.apache.tomcat.lite.io.IOInputStream;
+
+
+/**
+ * Wrapper around BufferInputStream.
+ */
+public final class ServletInputStreamImpl extends ServletInputStream {
+ private IOInputStream ib;
+
+ public ServletInputStreamImpl(IOInputStream ib) {
+ this.ib = ib;
+ }
+
+ public long skip(long n)
+ throws IOException {
+ return ib.skip(n);
+ }
+
+ public void mark(int readAheadLimit)
+ {
+ ib.mark(readAheadLimit);
+ }
+
+
+ public void reset()
+ throws IOException {
+ ib.reset();
+ }
+
+ public int read()
+ throws IOException {
+ return ib.read();
+ }
+
+ public int available() throws IOException {
+ return ib.available();
+ }
+
+ public int read(final byte[] b) throws IOException {
+ return ib.read(b, 0, b.length);
+ }
+
+
+ public int read(final byte[] b, final int off, final int len)
+ throws IOException {
+ return ib.read(b, off, len);
+ }
+
+
+ public int readLine(byte[] b, int off, int len) throws IOException {
+ return ib.readLine(b, off, len);
+ }
+
+ public void close() throws IOException {
+ // no call to super.close !
+ ib.close();
+ }
+
+}
--- /dev/null
+/*
+ * 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.servlet;
+
+import java.io.IOException;
+
+import javax.servlet.ServletOutputStream;
+
+import org.apache.tomcat.lite.io.IOOutputStream;
+
+
+/**
+ * Coyote implementation of the servlet output stream.
+ *
+ * @author Costin Manolache
+ * @author Remy Maucherat
+ */
+public final class ServletOutputStreamImpl extends ServletOutputStream {
+
+ private IOOutputStream ob;
+
+ public ServletOutputStreamImpl(IOOutputStream ob) {
+ this.ob = ob;
+ }
+
+ public void write(int i)
+ throws IOException {
+ ob.write(i);
+ }
+
+
+ public void write(byte[] b)
+ throws IOException {
+ write(b, 0, b.length);
+ }
+
+
+ public void write(byte[] b, int off, int len)
+ throws IOException {
+ ob.write(b, off, len);
+ }
+
+
+ /**
+ * Will send the buffer to the client.
+ */
+ public void flush()
+ throws IOException {
+ ob.flush();
+ }
+
+ public void close()
+ throws IOException {
+ ob.close();
+ }
+
+ public void print(String s)
+ throws IOException {
+ ob.print(s);
+ }
+}
+
--- /dev/null
+/*
+ * 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.servlet;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+
+
+
+/**
+ * Coyote implementation of the buffred reader.
+ *
+ * @author Remy Maucherat
+ */
+public final class ServletReaderImpl extends BufferedReader {
+
+ private BufferedReader ib;
+
+ public ServletReaderImpl(BufferedReader ib) {
+ super(ib, 1);
+ this.ib = ib;
+ }
+
+ public void close()
+ throws IOException {
+ ib.close();
+ }
+
+
+ public int read()
+ throws IOException {
+ return ib.read();
+ }
+
+
+ public int read(char[] cbuf)
+ throws IOException {
+ return ib.read(cbuf, 0, cbuf.length);
+ }
+
+
+ public int read(char[] cbuf, int off, int len)
+ throws IOException {
+ return ib.read(cbuf, off, len);
+ }
+
+
+ public long skip(long n)
+ throws IOException {
+ return ib.skip(n);
+ }
+
+
+ public boolean ready()
+ throws IOException {
+ return ib.ready();
+ }
+
+
+ public boolean markSupported() {
+ return true;
+ }
+
+
+ public void mark(int readAheadLimit)
+ throws IOException {
+ ib.mark(readAheadLimit);
+ }
+
+
+ public void reset()
+ throws IOException {
+ ib.reset();
+ }
+
+
+ // TODO: move the readLine functionality to base coyote IO
+ public String readLine()
+ throws IOException {
+ return ib.readLine();
+
+ }
+}
--- /dev/null
+/*
+ * 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.servlet;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.security.Principal;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.TreeMap;
+import java.util.logging.Level;
+
+import javax.security.auth.Subject;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletRequestAttributeEvent;
+import javax.servlet.ServletRequestAttributeListener;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.apache.tomcat.lite.http.HttpRequest;
+import org.apache.tomcat.lite.http.MappingData;
+import org.apache.tomcat.lite.http.MultiMap;
+import org.apache.tomcat.lite.http.ServerCookie;
+import org.apache.tomcat.lite.http.MultiMap.Entry;
+import org.apache.tomcat.lite.io.BBuffer;
+import org.apache.tomcat.lite.io.CBuffer;
+import org.apache.tomcat.lite.io.FastHttpDateFormat;
+import org.apache.tomcat.servlets.session.UserSessionManager;
+import org.apache.tomcat.servlets.util.Enumerator;
+import org.apache.tomcat.servlets.util.LocaleParser;
+import org.apache.tomcat.servlets.util.RequestUtil;
+
+
+/**
+ *
+ * Wrapper object for the request.
+ *
+ * @author Remy Maucherat
+ * @author Craig R. McClanahan
+ */
+public abstract class ServletRequestImpl implements HttpServletRequest {
+
+ /**
+ * The request attribute under which we store the array of X509Certificate
+ * objects representing the certificate chain presented by our client,
+ * if any.
+ */
+ public static final String CERTIFICATES_ATTR =
+ "javax.servlet.request.X509Certificate";
+
+ /**
+ * The request attribute under which we store the name of the cipher suite
+ * being used on an SSL connection (as an object of type
+ * java.lang.String).
+ */
+ public static final String CIPHER_SUITE_ATTR =
+ "javax.servlet.request.cipher_suite";
+
+ /**
+ * Request dispatcher state.
+ */
+ public static final String DISPATCHER_TYPE_ATTR =
+ "org.apache.catalina.core.DISPATCHER_TYPE";
+
+ /**
+ * Request dispatcher path.
+ */
+ public static final String DISPATCHER_REQUEST_PATH_ATTR =
+ "org.apache.catalina.core.DISPATCHER_REQUEST_PATH";
+
+ /**
+ * The servlet context attribute under which we store the class path
+ * for our application class loader (as an object of type String),
+ * delimited with the appropriate path delimiter for this platform.
+ */
+ public static final String CLASS_PATH_ATTR =
+ "org.apache.catalina.jsp_classpath";
+
+
+ /**
+ * The request attribute under which we forward a Java exception
+ * (as an object of type Throwable) to an error page.
+ */
+ public static final String EXCEPTION_ATTR =
+ "javax.servlet.error.exception";
+
+
+ /**
+ * The request attribute under which we forward the request URI
+ * (as an object of type String) of the page on which an error occurred.
+ */
+ public static final String EXCEPTION_PAGE_ATTR =
+ "javax.servlet.error.request_uri";
+
+
+ /**
+ * The request attribute under which we forward a Java exception type
+ * (as an object of type Class) to an error page.
+ */
+ public static final String EXCEPTION_TYPE_ATTR =
+ "javax.servlet.error.exception_type";
+
+
+ /**
+ * The request attribute under which we forward an HTTP status message
+ * (as an object of type STring) to an error page.
+ */
+ public static final String ERROR_MESSAGE_ATTR =
+ "javax.servlet.error.message";
+
+
+ /**
+ * The request attribute under which we expose the value of the
+ * <code><jsp-file></code> value associated with this servlet,
+ * if any.
+ */
+ public static final String JSP_FILE_ATTR =
+ "org.apache.catalina.jsp_file";
+
+
+ /**
+ * The request attribute under which we store the key size being used for
+ * this SSL connection (as an object of type java.lang.Integer).
+ */
+ public static final String KEY_SIZE_ATTR =
+ "javax.servlet.request.key_size";
+
+ /**
+ * The request attribute under which we store the session id being used
+ * for this SSL connection (as an object of type java.lang.String).
+ */
+ public static final String SSL_SESSION_ID_ATTR =
+ "javax.servlet.request.ssl_session";
+
+ /**
+ * The request attribute under which we forward a servlet name to
+ * an error page.
+ */
+ public static final String SERVLET_NAME_ATTR =
+ "javax.servlet.error.servlet_name";
+
+
+ /**
+ * The name of the cookie used to pass the session identifier back
+ * and forth with the client.
+ */
+ public static final String SESSION_COOKIE_NAME = "JSESSIONID";
+
+
+ /**
+ * The name of the path parameter used to pass the session identifier
+ * back and forth with the client.
+ */
+ public static final String SESSION_PARAMETER_NAME = "jsessionid";
+
+
+ /**
+ * The request attribute under which we forward an HTTP status code
+ * (as an object of type Integer) to an error page.
+ */
+ public static final String STATUS_CODE_ATTR =
+ "javax.servlet.error.status_code";
+
+
+ /**
+ * The subject under which the AccessControlContext is running.
+ */
+ public static final String SUBJECT_ATTR =
+ "javax.security.auth.subject";
+
+
+ /**
+ * The servlet context attribute under which we store a temporary
+ * working directory (as an object of type File) for use by servlets
+ * within this web application.
+ */
+ public static final String WORK_DIR_ATTR =
+ "javax.servlet.context.tempdir";
+
+ protected static final TimeZone GMT_ZONE = TimeZone.getTimeZone("GMT");
+
+
+ /**
+ * The default Locale if none are specified.
+ */
+ protected static Locale defaultLocale = Locale.getDefault();
+
+ // ApplicationFilterFactory. What's the use ???
+ private static Integer REQUEST_INTEGER = new Integer(8);
+
+ /**
+ * The match string for identifying a session ID parameter.
+ */
+ private static final String match = ";" + SESSION_PARAMETER_NAME + "=";
+
+ /**
+ * The set of cookies associated with this Request.
+ */
+ protected Cookie[] cookies = null;
+
+
+ /**
+ * The set of SimpleDateFormat formats to use in getDateHeader().
+ *
+ * Notice that because SimpleDateFormat is not thread-safe, we can't
+ * declare formats[] as a static variable.
+ */
+ protected SimpleDateFormat formats[] = null;
+
+
+ /**
+ * The attributes associated with this Request, keyed by attribute name.
+ */
+ protected HashMap attributes = new HashMap();
+
+ /**
+ * The preferred Locales assocaited with this Request.
+ */
+ protected ArrayList locales = new ArrayList();
+
+
+ /**
+ * Authentication type.
+ */
+ protected String authType = null;
+
+ /**
+ * User principal.
+ */
+ protected Principal userPrincipal = null;
+
+
+ /**
+ * The Subject associated with the current AccessControllerContext
+ */
+ protected transient Subject subject = null;
+
+
+ /**
+ * The current dispatcher type.
+ */
+ protected Object dispatcherType = null;
+
+ /**
+ * ServletInputStream.
+ */
+ protected ServletInputStreamImpl inputStream;
+
+
+ /**
+ * Using stream flag.
+ */
+ protected boolean usingInputStream = false;
+
+
+ /**
+ * Using writer flag.
+ */
+ protected boolean usingReader = false;
+
+
+ /**
+ * Session parsed flag.
+ */
+ protected boolean sessionParsed = false;
+
+
+ /**
+ * Secure flag.
+ */
+ protected boolean secure = false;
+
+
+ /**
+ * The currently active session for this request.
+ */
+ protected HttpSession session = null;
+
+
+ /**
+ * The current request dispatcher path.
+ */
+ protected Object requestDispatcherPath = null;
+
+
+ /**
+ * Was the requested session ID received in a cookie?
+ */
+ protected boolean requestedSessionCookie = false;
+
+
+ /**
+ * The requested session ID (if any) for this request.
+ */
+ protected String requestedSessionId = null;
+
+
+ /**
+ * Was the requested session ID received in a URL?
+ */
+ protected boolean requestedSessionURL = false;
+
+
+ /**
+ * Parse locales.
+ */
+ protected boolean localesParsed = false;
+
+
+ /**
+ * Associated context.
+ */
+ private ServletContextImpl context = null;
+
+
+
+ // --------------------------------------------------------- Public Methods
+
+ /**
+ * Filter chain associated with the request.
+ */
+ protected FilterChainImpl filterChain = new FilterChainImpl();
+
+
+ // -------------------------------------------------------- Request Methods
+
+ /**
+ * The response with which this request is associated.
+ */
+ protected ServletResponseImpl response = new ServletResponseImpl();
+
+ /**
+ * URI byte to char converter (not recycled).
+ */
+ // protected B2CConverter URIConverter = null;
+
+ /**
+ * Associated wrapper.
+ */
+ protected ServletConfigImpl wrapper = null;
+
+ private HttpRequest httpRequest;
+
+ /** New IO/buffer model
+ * @param req
+ */
+ //protected Http11Connection con;
+
+ ServletRequestImpl(HttpRequest req) {
+ setHttpRequest(req);
+ response.setRequest(this);
+ }
+
+
+// /**
+// * Return the Host within which this Request is being processed.
+// */
+// public Host getHost() {
+// if (getContext() == null)
+// return null;
+// return (Host) getContext().getParent();
+// //return ((Host) mappingData.host);
+// }
+//
+//
+// /**
+// * Set the Host within which this Request is being processed. This
+// * must be called as soon as the appropriate Host is identified, and
+// * before the Request is passed to a context.
+// *
+// * @param host The newly associated Host
+// */
+// public void setHost(Host host) {
+// mappingData.host = host;
+// }
+
+ /**
+ * Add a Header to the set of Headers associated with this Request.
+ *
+ * @param name The new header name
+ * @param value The new header value
+ */
+ public void addHeader(String name, String value) {
+ // Not used
+ }
+
+ /**
+ * Add a Locale to the set of preferred Locales for this Request. The
+ * first added Locale will be the first one returned by getLocales().
+ *
+ * @param locale The new preferred Locale
+ */
+ public void addLocale(Locale locale) {
+ locales.add(locale);
+ }
+
+
+ /**
+ * Add a parameter name and corresponding set of values to this Request.
+ * (This is used when restoring the original request on a form based
+ * login).
+ *
+ * @param name Name of this request parameter
+ * @param values Corresponding values for this request parameter
+ */
+ public void addParameter(String name, String values) {
+ httpRequest.addParameter(name, values);
+ }
+
+ /**
+ * Clear the collection of Headers associated with this Request.
+ */
+ public void clearHeaders() {
+ // Not used
+ }
+
+ /**
+ * Clear the collection of Locales associated with this Request.
+ */
+ public void clearLocales() {
+ locales.clear();
+ }
+
+ /**
+ * Clear the collection of parameters associated with this Request.
+ */
+ public void clearParameters() {
+ // Not used
+ }
+
+
+ /**
+ * Create and return a ServletInputStream to read the content
+ * associated with this Request.
+ *
+ * @exception IOException if an input/output error occurs
+ */
+ public ServletInputStream createInputStream()
+ throws IOException {
+ return inputStream;
+ }
+
+ /**
+ * Perform whatever actions are required to flush and close the input
+ * stream or reader, in a single operation.
+ *
+ * @exception IOException if an input/output error occurs
+ */
+ public void finishRequest() throws IOException {
+ // The reader and input stream don't need to be closed
+ }
+
+
+ /**
+ * Return the specified request attribute if it exists; otherwise, return
+ * <code>null</code>.
+ *
+ * @param name Name of the request attribute to return
+ */
+ public Object getAttribute(String name) {
+
+ if (name.equals(ServletRequestImpl.DISPATCHER_TYPE_ATTR)) {
+ return (dispatcherType == null)
+ ? REQUEST_INTEGER
+ : dispatcherType;
+ } else if (name.equals(ServletRequestImpl.DISPATCHER_REQUEST_PATH_ATTR)) {
+ return (requestDispatcherPath == null)
+ ? getMappingData().requestPath.toString()
+ : requestDispatcherPath.toString();
+ }
+
+ Object attr=attributes.get(name);
+
+ if(attr!=null)
+ return(attr);
+
+// attr = reqB.getAttribute(name);
+// if(attr != null)
+// return attr;
+// if( isSSLAttribute(name) ) {
+// reqB.action(ActionCode.ACTION_REQ_SSL_ATTRIBUTE,
+// reqB);
+// attr = reqB.getAttribute(ServletRequestImpl.CERTIFICATES_ATTR);
+// if( attr != null) {
+// attributes.put(ServletRequestImpl.CERTIFICATES_ATTR, attr);
+// }
+// attr = reqB.getAttribute(ServletRequestImpl.CIPHER_SUITE_ATTR);
+// if(attr != null) {
+// attributes.put(ServletRequestImpl.CIPHER_SUITE_ATTR, attr);
+// }
+// attr = reqB.getAttribute(ServletRequestImpl.KEY_SIZE_ATTR);
+// if(attr != null) {
+// attributes.put(ServletRequestImpl.KEY_SIZE_ATTR, attr);
+// }
+// attr = reqB.getAttribute(ServletRequestImpl.SSL_SESSION_ID_ATTR);
+// if(attr != null) {
+// attributes.put(ServletRequestImpl.SSL_SESSION_ID_ATTR, attr);
+// }
+// attr = attributes.get(name);
+// }
+ return attr;
+ }
+
+ /**
+ * Return the names of all request attributes for this Request, or an
+ * empty <code>Enumeration</code> if there are none.
+ */
+ public Enumeration getAttributeNames() {
+ if (isSecure()) {
+ getAttribute(ServletRequestImpl.CERTIFICATES_ATTR);
+ }
+ return new Enumerator(attributes.keySet(), true);
+ }
+
+
+ /**
+ * Return the authentication type used for this Request.
+ */
+ public String getAuthType() {
+ return (authType);
+ }
+
+
+ // ------------------------------------------------- Request Public Methods
+
+
+ /**
+ * Return the character encoding for this Request.
+ */
+ public String getCharacterEncoding() {
+ return (httpRequest.getCharacterEncoding());
+ }
+
+
+ /**
+ * Return the content length for this Request.
+ */
+ public int getContentLength() {
+ return ((int) httpRequest.getContentLength());
+ }
+
+
+// /**
+// * Return the object bound with the specified name to the internal notes
+// * for this request, or <code>null</code> if no such binding exists.
+// *
+// * @param name Name of the note to be returned
+// */
+// public Object getNote(String name) {
+// return (notes.get(name));
+// }
+//
+//
+// /**
+// * Return an Iterator containing the String names of all notes bindings
+// * that exist for this request.
+// */
+// public Iterator getNoteNames() {
+// return (notes.keySet().iterator());
+// }
+//
+//
+// /**
+// * Remove any object bound to the specified name in the internal notes
+// * for this request.
+// *
+// * @param name Name of the note to be removed
+// */
+// public void removeNote(String name) {
+// notes.remove(name);
+// }
+//
+//
+// /**
+// * Bind an object to a specified name in the internal notes associated
+// * with this request, replacing any existing binding for this name.
+// *
+// * @param name Name to which the object should be bound
+// * @param value Object to be bound to the specified name
+// */
+// public void setNote(String name, Object value) {
+// notes.put(name, value);
+// }
+//
+
+ /**
+ * Return the content type for this Request.
+ */
+ public String getContentType() {
+ return (httpRequest.getContentType());
+ }
+
+
+ /**
+ * Return the Context within which this Request is being processed.
+ */
+ public ServletContextImpl getContext() {
+ if (context == null) {
+ context = (ServletContextImpl) httpRequest.getMappingData().context;
+ }
+ return (this.context);
+ }
+
+
+ /**
+ * Return the portion of the request URI used to select the Context
+ * of the Request.
+ */
+ public String getContextPath() {
+ return (getMappingData().contextPath.toString());
+ }
+
+
+ /**
+ * Return the set of Cookies received with this Request.
+ */
+ public Cookie[] getCookies() {
+ if (cookies == null) {
+ List<ServerCookie> serverCookies = httpRequest.getServerCookies();
+ if (serverCookies.size() == 0) {
+ return null;
+ }
+ cookies = new Cookie[serverCookies.size()];
+ for (int i = 0; i < serverCookies.size(); i++) {
+ ServerCookie scookie = serverCookies.get(i);
+ try {
+ // TODO: we could override all methods and
+ // return recyclable cookies, if we really wanted
+ Cookie cookie = new Cookie(scookie.getName().toString(),
+ scookie.getValue().toString());
+ cookie.setPath(scookie.getPath().toString());
+ cookie.setVersion(scookie.getVersion());
+ String domain = scookie.getDomain().toString();
+ if (domain != null) {
+ cookie.setDomain(scookie.getDomain().toString());
+ }
+ cookies[i] = cookie;
+ } catch(IllegalArgumentException e) {
+ // Ignore bad cookie
+ }
+ }
+ }
+ return cookies;
+ }
+
+
+ /**
+ * Return the value of the specified date header, if any; otherwise
+ * return -1.
+ *
+ * @param name Name of the requested date header
+ *
+ * @exception IllegalArgumentException if the specified header value
+ * cannot be converted to a date
+ */
+ public long getDateHeader(String name) {
+
+ String value = getHeader(name);
+ if (value == null)
+ return (-1L);
+ if (formats == null) {
+ formats = new SimpleDateFormat[] {
+ new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
+ new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
+ new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
+ };
+ formats[0].setTimeZone(GMT_ZONE);
+ formats[1].setTimeZone(GMT_ZONE);
+ formats[2].setTimeZone(GMT_ZONE);
+ }
+
+ // Attempt to convert the date header in a variety of formats
+ long result = FastHttpDateFormat.parseDate(value, formats);
+ if (result != (-1L)) {
+ return result;
+ }
+ throw new IllegalArgumentException(value);
+
+ }
+
+ /**
+ * Get filter chain associated with the request.
+ */
+ public FilterChainImpl getFilterChain() {
+ return (this.filterChain);
+ }
+
+
+ // ------------------------------------------------- ServletRequest Methods
+
+ /**
+ * Return the first value of the specified header, if any; otherwise,
+ * return <code>null</code>
+ *
+ * @param name Name of the requested header
+ */
+ public String getHeader(String name) {
+ return httpRequest.getHeader(name);
+ }
+
+ /**
+ * Return the names of all headers received with this request.
+ */
+ public Enumeration getHeaderNames() {
+ return httpRequest.getMimeHeaders().names();
+ }
+
+
+ /**
+ * Return all of the values of the specified header, if any; otherwise,
+ * return an empty enumeration.
+ *
+ * @param name Name of the requested header
+ */
+ public Enumeration getHeaders(String name) {
+ Entry entry = httpRequest.getMimeHeaders().getEntry(name);
+ if (entry == null) {
+ return MultiMap.EMPTY;
+ }
+ return new MultiMap.IteratorEnumerator(entry.values.iterator());
+ }
+
+ /**
+ * Return the servlet input stream for this Request. The default
+ * implementation returns a servlet input stream created by
+ * <code>createInputStream()</code>.
+ *
+ * @exception IllegalStateException if <code>getReader()</code> has
+ * already been called for this request
+ * @exception IOException if an input/output error occurs
+ */
+ public ServletInputStream getInputStream() throws IOException {
+
+ if (usingReader)
+ throw new IllegalStateException
+ ("usingReader");
+
+ usingInputStream = true;
+ return inputStream;
+
+ }
+
+
+ /**
+ * Return the value of the specified header as an integer, or -1 if there
+ * is no such header for this request.
+ *
+ * @param name Name of the requested header
+ *
+ * @exception IllegalArgumentException if the specified header value
+ * cannot be converted to an integer
+ */
+ public int getIntHeader(String name) {
+
+ String value = getHeader(name);
+ if (value == null) {
+ return (-1);
+ } else {
+ return (Integer.parseInt(value));
+ }
+
+ }
+
+
+ /**
+ * Returns the Internet Protocol (IP) address of the interface on
+ * which the request was received.
+ */
+ public String getLocalAddr(){
+ return httpRequest.localAddr().toString();
+ }
+
+
+ /**
+ * Return the preferred Locale that the client will accept content in,
+ * based on the value for the first <code>Accept-Language</code> header
+ * that was encountered. If the request did not specify a preferred
+ * language, the server's default Locale is returned.
+ */
+ public Locale getLocale() {
+
+ if (!localesParsed)
+ parseLocales();
+
+ if (locales.size() > 0) {
+ return ((Locale) locales.get(0));
+ } else {
+ return (defaultLocale);
+ }
+
+ }
+
+
+ /**
+ * Return the set of preferred Locales that the client will accept
+ * content in, based on the values for any <code>Accept-Language</code>
+ * headers that were encountered. If the request did not specify a
+ * preferred language, the server's default Locale is returned.
+ */
+ public Enumeration getLocales() {
+
+ if (!localesParsed)
+ parseLocales();
+
+ if (locales.size() > 0)
+ return (new Enumerator(locales));
+ ArrayList results = new ArrayList();
+ results.add(defaultLocale);
+ return (new Enumerator(results));
+
+ }
+
+
+ /**
+ * Returns the host name of the Internet Protocol (IP) interface on
+ * which the request was received.
+ */
+ public String getLocalName(){
+ return httpRequest.localName().toString();
+ }
+
+
+ /**
+ * Returns the Internet Protocol (IP) port number of the interface
+ * on which the request was received.
+ */
+ public int getLocalPort(){
+ return httpRequest.getLocalPort();
+ }
+
+ /**
+ * Return the server port responding to this Request.
+ */
+ public int getServerPort() {
+ return (httpRequest.getServerPort());
+ }
+
+ /**
+ * Return mapping data.
+ */
+ public MappingData getMappingData() {
+ return (httpRequest.getMappingData());
+ }
+
+
+
+ /**
+ * Return the HTTP request method used in this Request.
+ */
+ public String getMethod() {
+ return httpRequest.method().toString();
+ }
+
+
+ /**
+ * Return the value of the specified request parameter, if any; otherwise,
+ * return <code>null</code>. If there is more than one value defined,
+ * return only the first one.
+ *
+ * @param name Name of the desired request parameter
+ */
+ public String getParameter(String name) {
+ return httpRequest.getParameter(name);
+
+ }
+
+
+ /**
+ * Returns a <code>Map</code> of the parameters of this request.
+ * Request parameters are extra information sent with the request.
+ * For HTTP servlets, parameters are contained in the query string
+ * or posted form data.
+ *
+ * @return A <code>Map</code> containing parameter names as keys
+ * and parameter values as map values.
+ */
+ public Map<String, String[]> getParameterMap() {
+ return httpRequest.getParameterMap();
+ }
+
+
+ /**
+ * Return the names of all defined request parameters for this request.
+ */
+ public Enumeration getParameterNames() {
+ return httpRequest.getParameterNames();
+
+ }
+
+
+ /**
+ * Return the defined values for the specified request parameter, if any;
+ * otherwise, return <code>null</code>.
+ *
+ * @param name Name of the desired request parameter
+ */
+ public String[] getParameterValues(String name) {
+ return httpRequest.getParameterValues(name);
+
+ }
+
+
+ /**
+ * Return the path information associated with this Request.
+ */
+ public String getPathInfo() {
+ CBuffer pathInfo = getMappingData().pathInfo;
+ if (pathInfo.length() == 0) {
+ return null;
+ }
+ return (getMappingData().pathInfo.toString());
+ }
+
+
+ /**
+ * Return the extra path information for this request, translated
+ * to a real path.
+ */
+ public String getPathTranslated() {
+
+ if (getContext() == null)
+ return (null);
+
+ if (getPathInfo() == null) {
+ return (null);
+ } else {
+ return (getContext().getServletContext().getRealPath(getPathInfo()));
+ }
+
+ }
+
+ /**
+ * Return the principal that has been authenticated for this Request.
+ */
+ public Principal getPrincipal() {
+ return (userPrincipal);
+ }
+
+ /**
+ * Return the protocol and version used to make this Request.
+ */
+ public String getProtocol() {
+ return httpRequest.protocol().toString();
+ }
+
+ /**
+ * Return the query string associated with this request.
+ */
+ public String getQueryString() {
+ String queryString = httpRequest.queryString().toString();
+ if (queryString == null || queryString.equals("")) {
+ return (null);
+ } else {
+ return queryString;
+ }
+ }
+
+
+ /**
+ * Read the Reader wrapping the input stream for this Request. The
+ * default implementation wraps a <code>BufferedReader</code> around the
+ * servlet input stream returned by <code>createInputStream()</code>.
+ *
+ * @exception IllegalStateException if <code>getInputStream()</code>
+ * has already been called for this request
+ * @exception IOException if an input/output error occurs
+ */
+ public BufferedReader getReader() throws IOException {
+
+ if (usingInputStream)
+ throw new IllegalStateException
+ ("usingInputStream");
+
+ usingReader = true;
+ return httpRequest.getReader();
+
+ }
+
+ /**
+ * Return the real path of the specified virtual path.
+ *
+ * @param path Path to be translated
+ *
+ * @deprecated As of version 2.1 of the Java Servlet API, use
+ * <code>ServletContext.getRealPath()</code>.
+ */
+ public String getRealPath(String path) {
+
+ if (getContext() == null)
+ return (null);
+ ServletContext servletContext = getContext(); // .getServletContext();
+ if (servletContext == null)
+ return (null);
+ else {
+ try {
+ return (servletContext.getRealPath(path));
+ } catch (IllegalArgumentException e) {
+ return (null);
+ }
+ }
+
+ }
+
+
+ /**
+ * Return the remote IP address making this Request.
+ */
+ public String getRemoteAddr() {
+ return httpRequest.remoteAddr().toString();
+ }
+
+
+ /**
+ * Return the remote host name making this Request.
+ */
+ public String getRemoteHost() {
+ return httpRequest.remoteHost().toString();
+ }
+
+
+ /**
+ * Returns the Internet Protocol (IP) source port of the client
+ * or last proxy that sent the request.
+ */
+ public int getRemotePort(){
+ return httpRequest.getRemotePort();
+ }
+
+
+ /**
+ * Return the name of the remote user that has been authenticated
+ * for this Request.
+ */
+ public String getRemoteUser() {
+
+ if (userPrincipal != null) {
+ return (userPrincipal.getName());
+ } else {
+ return (null);
+ }
+
+ }
+
+
+ /**
+ * Return the <code>ServletRequest</code> for which this object
+ * is the facade. This method must be implemented by a subclass.
+ */
+ public HttpServletRequest getRequest() {
+ return this;
+ }
+
+ public HttpRequest getHttpRequest() {
+ return httpRequest;
+ }
+
+ public void setHttpRequest(HttpRequest req) {
+ this.httpRequest = req;
+ inputStream = new ServletInputStreamImpl(req.getBodyInputStream());
+ }
+
+ /**
+ * Return a RequestDispatcher that wraps the resource at the specified
+ * path, which may be interpreted as relative to the current request path.
+ *
+ * @param path Path of the resource to be wrapped
+ */
+ public RequestDispatcher getRequestDispatcher(String path) {
+
+ if (getContext() == null)
+ return (null);
+
+ // If the path is already context-relative, just pass it through
+ if (path == null)
+ return (null);
+ else if (path.startsWith("/"))
+ return (getContext().getRequestDispatcher(path));
+
+ // Convert a request-relative path to a context-relative one
+ String servletPath = (String) getAttribute(RequestDispatcherImpl.INCLUDE_SERVLET_PATH_ATTR);
+ if (servletPath == null)
+ servletPath = getServletPath();
+
+ // Add the path info, if there is any
+ String pathInfo = getPathInfo();
+ String requestPath = null;
+
+ if (pathInfo == null) {
+ requestPath = servletPath;
+ } else {
+ requestPath = servletPath + pathInfo;
+ }
+
+ int pos = requestPath.lastIndexOf('/');
+ String relative = null;
+ if (pos >= 0) {
+ relative = RequestUtil.normalize
+ (requestPath.substring(0, pos + 1) + path);
+ } else {
+ relative = RequestUtil.normalize(requestPath + path);
+ }
+
+ return (getContext().getRequestDispatcher(relative));
+
+ }
+
+
+ /**
+ * Return the session identifier included in this request, if any.
+ */
+ public String getRequestedSessionId() {
+ return (requestedSessionId);
+ }
+
+
+ // ---------------------------------------------------- HttpRequest Methods
+
+
+ /**
+ * Return the request URI for this request.
+ */
+ public String getRequestURI() {
+ return httpRequest.requestURI().toString();
+ }
+
+ /**
+ * Reconstructs the URL the client used to make the request.
+ * The returned URL contains a protocol, server name, port
+ * number, and server path, but it does not include query
+ * string parameters.
+ * <p>
+ * Because this method returns a <code>StringBuffer</code>,
+ * not a <code>String</code>, you can modify the URL easily,
+ * for example, to append query parameters.
+ * <p>
+ * This method is useful for creating redirect messages and
+ * for reporting errors.
+ *
+ * @return A <code>StringBuffer</code> object containing the
+ * reconstructed URL
+ */
+ public StringBuffer getRequestURL() {
+
+ StringBuffer url = new StringBuffer();
+ String scheme = getScheme();
+ int port = getServerPort();
+ if (port < 0)
+ port = 80; // Work around java.net.URL bug
+
+ url.append(scheme);
+ url.append("://");
+ url.append(getServerName());
+ if ((scheme.equals("http") && (port != 80))
+ || (scheme.equals("https") && (port != 443))) {
+ url.append(':');
+ url.append(port);
+ }
+ url.append(getRequestURI());
+
+ return (url);
+
+ }
+
+
+ /**
+ * Return the Response with which this Request is associated.
+ */
+ public ServletResponseImpl getResponse() {
+ return (this.response);
+ }
+
+
+ /**
+ * Return the scheme used to make this Request.
+ */
+ public String getScheme() {
+ String scheme = httpRequest.scheme().toString();
+ if (scheme == null) {
+ scheme = (isSecure() ? "https" : "http");
+ }
+ return scheme;
+ }
+
+
+ /**
+ * Return the server name responding to this Request.
+ */
+ public String getServerName() {
+ return httpRequest.getServerName();
+ }
+
+
+
+ /**
+ * Return the portion of the request URI used to select the servlet
+ * that will process this request.
+ */
+ public String getServletPath() {
+ return (getMappingData().wrapperPath.toString());
+ }
+
+ /**
+ * Return the input stream associated with this Request.
+ */
+ public InputStream getStream() {
+ return inputStream;
+ }
+
+
+ /**
+ * Return the principal that has been authenticated for this Request.
+ */
+ public Principal getUserPrincipal() {
+ return userPrincipal;
+ }
+
+
+ /**
+ * Return the Wrapper within which this Request is being processed.
+ */
+ public ServletConfigImpl getWrapper() {
+ return (this.wrapper);
+ }
+
+
+ /**
+ * Return <code>true</code> if the session identifier included in this
+ * request came from a cookie.
+ */
+ public boolean isRequestedSessionIdFromCookie() {
+
+ if (requestedSessionId != null)
+ return (requestedSessionCookie);
+ else
+ return (false);
+
+ }
+
+
+ /**
+ * Return <code>true</code> if the session identifier included in this
+ * request came from the request URI.
+ *
+ * @deprecated As of Version 2.1 of the Java Servlet API, use
+ * <code>isRequestedSessionIdFromURL()</code> instead.
+ */
+ public boolean isRequestedSessionIdFromUrl() {
+ return (isRequestedSessionIdFromURL());
+ }
+
+
+ /**
+ * Return <code>true</code> if the session identifier included in this
+ * request came from the request URI.
+ */
+ public boolean isRequestedSessionIdFromURL() {
+
+ if (requestedSessionId != null)
+ return (requestedSessionURL);
+ else
+ return (false);
+
+ }
+
+
+ /**
+ * Return <code>true</code> if the session identifier included in this
+ * request identifies a valid session.
+ */
+ public boolean isRequestedSessionIdValid() {
+
+ if (requestedSessionId == null)
+ return (false);
+ if (getContext() == null)
+ return (false);
+ UserSessionManager manager = getContext().getManager();
+ if (manager == null)
+ return (false);
+ HttpSession session = null;
+ try {
+ session = manager.findSession(requestedSessionId);
+ } catch (IOException e) {
+ session = null;
+ }
+ if ((session != null) && manager.isValid(session))
+ return (true);
+ else
+ return (false);
+
+ }
+
+ /**
+ * Was this request received on a secure connection?
+ */
+ public boolean isSecure() {
+ return (secure);
+ }
+
+
+ /**
+ * Return <code>true</code> if the authenticated user principal
+ * possesses the specified role name.
+ *
+ * @param role Role name to be validated
+ */
+ public boolean isUserInRole(String role) {
+ // Have we got an authenticated principal at all?
+ Principal userPrincipal = getPrincipal();
+ if (userPrincipal == null)
+ return (false);
+
+ // Identify the Realm we will use for checking role assignmenets
+ if (getContext() == null)
+ return (false);
+
+ // Check for a role alias defined in a <security-role-ref> element
+ if (wrapper != null) {
+ String realRole = wrapper.getSecurityRoleRef(role);
+ if (realRole != null) {
+ role = realRole;
+ }
+ }
+
+ if (role.equals(userPrincipal.getName())) {
+ return true;
+ }
+
+ // TODO: check !!!!
+ // Check for a role defined directly as a <security-role>
+ return false;
+ }
+
+ /**
+ * Release all object references, and initialize instance variables, in
+ * preparation for reuse of this object.
+ */
+ void recycle() {
+
+ wrapper = null;
+
+ dispatcherType = null;
+ requestDispatcherPath = null;
+
+ authType = null;
+ usingInputStream = false;
+ usingReader = false;
+ userPrincipal = null;
+ subject = null;
+ sessionParsed = false;
+ locales.clear();
+ localesParsed = false;
+ secure = false;
+
+ attributes.clear();
+ //notes.clear();
+ cookies = null;
+
+ if (session != null) {
+ getContext().getManager().endAccess(session);
+ }
+ setContext(null);
+ session = null;
+ requestedSessionCookie = false;
+ requestedSessionId = null;
+ requestedSessionURL = false;
+
+ //getMappingData().recycle();
+ // httpRequest.recycle();
+
+ response.recycle();
+ }
+
+
+
+ /**
+ * Remove the specified request attribute if it exists.
+ *
+ * @param name Name of the request attribute to remove
+ */
+ public void removeAttribute(String name) {
+ Object value = null;
+ boolean found = false;
+
+ // Remove the specified attribute
+ // Check for read only attribute
+ // requests are per thread so synchronization unnecessary
+// if (readOnlyAttributes.containsKey(name)) {
+// return;
+// }
+ found = attributes.containsKey(name);
+ if (found) {
+ value = attributes.get(name);
+ attributes.remove(name);
+ } else {
+ return;
+ }
+
+ // Notify interested application event listeners
+ List listeners = getContext().getListeners();
+ if (listeners.size() == 0)
+ return;
+ ServletRequestAttributeEvent event = null;
+ for (int i = 0; i < listeners.size(); i++) {
+ if (!(listeners.get(i) instanceof ServletRequestAttributeListener))
+ continue;
+ ServletRequestAttributeListener listener =
+ (ServletRequestAttributeListener) listeners.get(i);
+ try {
+ if (event == null) {
+ event =
+ new ServletRequestAttributeEvent(getContext().getServletContext(),
+ getRequest(), name, value);
+ }
+ listener.attributeRemoved(event);
+ } catch (Throwable t) {
+ getContext().getLogger().log(Level.WARNING, "ServletRequestAttributeListner.attributeRemoved()", t);
+ // Error valve will pick this execption up and display it to user
+ attributes.put( ServletRequestImpl.EXCEPTION_ATTR, t );
+ }
+ }
+ }
+
+
+ /**
+ * Set the specified request attribute to the specified value.
+ *
+ * @param name Name of the request attribute to set
+ * @param value The associated value
+ */
+ public void setAttribute(String name, Object value) {
+
+ // Name cannot be null
+ if (name == null)
+ throw new IllegalArgumentException
+ ("setAttribute() name == null");
+
+ // Null value is the same as removeAttribute()
+ if (value == null) {
+ removeAttribute(name);
+ return;
+ }
+
+ if (name.equals(ServletRequestImpl.DISPATCHER_TYPE_ATTR)) {
+ dispatcherType = value;
+ return;
+ } else if (name.equals(ServletRequestImpl.DISPATCHER_REQUEST_PATH_ATTR)) {
+ requestDispatcherPath = value;
+ return;
+ }
+
+ Object oldValue = null;
+ boolean replaced = false;
+
+ // Add or replace the specified attribute
+ // Check for read only attribute
+ // requests are per thread so synchronization unnecessary
+// if (readOnlyAttributes.containsKey(name)) {
+// return;
+// }
+
+ oldValue = attributes.put(name, value);
+ if (oldValue != null) {
+ replaced = true;
+ }
+
+ // Pass special attributes to the native layer
+// if (name.startsWith("org.apache.tomcat.")) {
+// reqB.setAttribute(name, value);
+// }
+//
+ // Notify interested application event listeners
+ List listeners = getContext().getListeners();
+ if (listeners.size() == 0)
+ return;
+ ServletRequestAttributeEvent event = null;
+
+ for (int i = 0; i < listeners.size(); i++) {
+ if (!(listeners.get(i) instanceof ServletRequestAttributeListener))
+ continue;
+ ServletRequestAttributeListener listener =
+ (ServletRequestAttributeListener) listeners.get(i);
+ try {
+ if (event == null) {
+ if (replaced)
+ event =
+ new ServletRequestAttributeEvent(getContext().getServletContext(),
+ getRequest(), name, oldValue);
+ else
+ event =
+ new ServletRequestAttributeEvent(getContext().getServletContext(),
+ getRequest(), name, value);
+ }
+ if (replaced) {
+ listener.attributeReplaced(event);
+ } else {
+ listener.attributeAdded(event);
+ }
+ } catch (Throwable t) {
+ getContext().getLogger().log(Level.WARNING, "ServletRequestAttributeListener error", t);
+ // Error valve will pick this execption up and display it to user
+ attributes.put( ServletRequestImpl.EXCEPTION_ATTR, t );
+ }
+ }
+ }
+
+
+ // --------------------------------------------- HttpServletRequest Methods
+
+
+ /**
+ * Set the authentication type used for this request, if any; otherwise
+ * set the type to <code>null</code>. Typical values are "BASIC",
+ * "DIGEST", or "SSL".
+ *
+ * @param type The authentication type used
+ */
+ public void setAuthType(String type) {
+ this.authType = type;
+ }
+
+
+ /**
+ * Overrides the name of the character encoding used in the body of
+ * this request. This method must be called prior to reading request
+ * parameters or reading input using <code>getReader()</code>.
+ *
+ * @param enc The character encoding to be used
+ *
+ * @exception UnsupportedEncodingException if the specified encoding
+ * is not supported
+ *
+ * @since Servlet 2.3
+ */
+ public void setCharacterEncoding(String enc)
+ throws UnsupportedEncodingException {
+
+ // Ensure that the specified encoding is valid
+ byte buffer[] = new byte[1];
+ buffer[0] = (byte) 'a';
+ String dummy = new String(buffer, enc);
+
+ // Save the validated encoding
+ httpRequest.setCharacterEncoding(enc);
+
+ }
+
+ /**
+ * Set the Context within which this Request is being processed. This
+ * must be called as soon as the appropriate Context is identified, because
+ * it identifies the value to be returned by <code>getContextPath()</code>,
+ * and thus enables parsing of the request URI.
+ *
+ * @param context The newly associated Context
+ */
+ public void setContext(ServletContextImpl context) {
+ this.context = context;
+ }
+
+
+ /**
+ * Set the context path for this Request. This will normally be called
+ * when the associated Context is mapping the Request to a particular
+ * Wrapper.
+ *
+ * @param path The context path
+ */
+ public void setContextPath(String path) {
+
+ if (path == null) {
+ getMappingData().contextPath.set("");
+ } else {
+ getMappingData().contextPath.set(path);
+ }
+
+ }
+
+ /**
+ * Set the path information for this Request. This will normally be called
+ * when the associated Context is mapping the Request to a particular
+ * Wrapper.
+ *
+ * @param path The path information
+ */
+ public void setPathInfo(String path) {
+ getMappingData().pathInfo.set(path);
+ }
+
+
+ /**
+ * Set a flag indicating whether or not the requested session ID for this
+ * request came in through a cookie. This is normally called by the
+ * HTTP Connector, when it parses the request headers.
+ *
+ * @param flag The new flag
+ */
+ public void setRequestedSessionCookie(boolean flag) {
+
+ this.requestedSessionCookie = flag;
+
+ }
+
+
+ /**
+ * Set the requested session ID for this request. This is normally called
+ * by the HTTP Connector, when it parses the request headers.
+ *
+ * @param id The new session id
+ */
+ public void setRequestedSessionId(String id) {
+
+ this.requestedSessionId = id;
+
+ }
+
+
+ /**
+ * Set a flag indicating whether or not the requested session ID for this
+ * request came in through a URL. This is normally called by the
+ * HTTP Connector, when it parses the request headers.
+ *
+ * @param flag The new flag
+ */
+ public void setRequestedSessionURL(boolean flag) {
+
+ this.requestedSessionURL = flag;
+
+ }
+
+
+ /**
+ * Set the value to be returned by <code>isSecure()</code>
+ * for this Request.
+ *
+ * @param secure The new isSecure value
+ */
+ public void setSecure(boolean secure) {
+ this.secure = secure;
+ }
+
+ /**
+ * Set the servlet path for this Request. This will normally be called
+ * when the associated Context is mapping the Request to a particular
+ * Wrapper.
+ *
+ * @param path The servlet path
+ */
+ public void setServletPath(String path) {
+ if (path != null)
+ getMappingData().wrapperPath.set(path);
+ }
+
+
+ /**
+ * Set the input stream associated with this Request.
+ *
+ * @param stream The new input stream
+ */
+ public void setStream(InputStream stream) {
+ // Ignore
+ }
+
+
+ /**
+ * Set the Principal who has been authenticated for this Request. This
+ * value is also used to calculate the value to be returned by the
+ * <code>getRemoteUser()</code> method.
+ *
+ * @param principal The user Principal
+ */
+ public void setUserPrincipal(Principal principal) {
+
+ if (System.getSecurityManager() != null){
+ HttpSession session = getSession(false);
+ if ( (subject != null) &&
+ (!subject.getPrincipals().contains(principal)) ){
+ subject.getPrincipals().add(principal);
+ } else if (session != null &&
+ session.getAttribute(ServletRequestImpl.SUBJECT_ATTR) == null) {
+ subject = new Subject();
+ subject.getPrincipals().add(principal);
+ }
+ if (session != null){
+ session.setAttribute(ServletRequestImpl.SUBJECT_ATTR, subject);
+ }
+ }
+
+ this.userPrincipal = principal;
+ }
+
+
+ /**
+ * Set the Wrapper within which this Request is being processed. This
+ * must be called as soon as the appropriate Wrapper is identified, and
+ * before the Request is ultimately passed to an application servlet.
+ * @param wrapper The newly associated Wrapper
+ */
+ public void setWrapper(ServletConfigImpl wrapper) {
+ this.wrapper = wrapper;
+ }
+
+
+ public String toString() {
+ return httpRequest.requestURI().toString();
+ }
+
+
+ /**
+ * Configures the given JSESSIONID cookie.
+ *
+ * @param cookie The JSESSIONID cookie to be configured
+ */
+ protected void configureSessionCookie(Cookie cookie) {
+ cookie.setMaxAge(-1);
+ String contextPath = null;
+ if (//!connector.getEmptySessionPath() &&
+ (getContext() != null)) {
+ contextPath = getContext().getEncodedPath();
+ }
+ if ((contextPath != null) && (contextPath.length() > 0)) {
+ cookie.setPath(contextPath);
+ } else {
+ cookie.setPath("/");
+ }
+ if (isSecure()) {
+ cookie.setSecure(true);
+ }
+ }
+
+
+ /**
+ * Return the session associated with this Request, creating one
+ * if necessary.
+ */
+ public HttpSession getSession() {
+ return getSession(true);
+ }
+
+
+ public HttpSession getSession(boolean create) {
+
+ // There cannot be a session if no context has been assigned yet
+ if (getContext() == null)
+ return (null);
+
+
+ // Return the requested session if it exists and is valid
+ UserSessionManager manager = null;
+ if (getContext() != null)
+ manager = getContext().getManager();
+ if (manager == null)
+ return (null); // Sessions are not supported
+
+ // Return the current session if it exists and is valid
+ if ((session != null) && !manager.isValid(session))
+ session = null;
+ if (session != null)
+ return (session);
+
+
+ if (requestedSessionId != null) {
+ try {
+ session = manager.findSession(requestedSessionId);
+ } catch (IOException e) {
+ session = null;
+ }
+ if ((session != null) && !manager.isValid(session))
+ session = null;
+ if (session != null) {
+ manager.access(session);
+ return (session);
+ }
+ }
+
+ // Create a new session if requested and the response is not committed
+ if (!create)
+ return (null);
+ if ((getContext() != null) && (response != null) &&
+ getContext().getCookies() &&
+ getResponse().isCommitted()) {
+ throw new IllegalStateException
+ ("isCommited()");
+ }
+
+ // Attempt to reuse session id if one was submitted in a cookie
+ // Do not reuse the session id if it is from a URL, to prevent possible
+ // phishing attacks
+ if (// connector.getEmptySessionPath() &&
+ isRequestedSessionIdFromCookie()) {
+ session = manager.createSession(getRequestedSessionId());
+ } else {
+ session = manager.createSession(null);
+ }
+
+ // Creating a new session cookie based on that session
+ if ((session != null) && (getContext() != null)
+ && getContext().getCookies()) {
+ Cookie cookie = new Cookie(ServletRequestImpl.SESSION_COOKIE_NAME,
+ session.getId());
+ configureSessionCookie(cookie);
+ response.addCookie(cookie);
+ }
+
+ if (session != null) {
+ manager.access(session);
+ return (session);
+ } else {
+ return (null);
+ }
+
+ }
+
+
+ /**
+ * Parse request locales.
+ */
+ protected void parseLocales() {
+
+ localesParsed = true;
+
+ Enumeration values = getHeaders("accept-language");
+
+ while (values.hasMoreElements()) {
+ String value = values.nextElement().toString();
+ parseLocalesHeader(value);
+ }
+
+ }
+
+ /**
+ * Parse accept-language header value.
+ */
+ protected void parseLocalesHeader(String value) {
+
+ TreeMap locales = new LocaleParser().parseLocale(value);
+ // Process the quality values in highest->lowest order (due to
+ // negating the Double value when creating the key)
+ Iterator keys = locales.keySet().iterator();
+ while (keys.hasNext()) {
+ Double key = (Double) keys.next();
+ ArrayList list = (ArrayList) locales.get(key);
+ Iterator values = list.iterator();
+ while (values.hasNext()) {
+ Locale locale = (Locale) values.next();
+ addLocale(locale);
+ }
+ }
+
+ }
+
+
+ /**
+ * Parse session id in URL. Done in request for performance.
+ * TODO: should be done in manager
+ */
+ protected void parseSessionCookiesId() {
+ String sessionCookieName = getContext().getSessionCookieName();
+
+ // Parse session id from cookies
+ ServerCookie scookie =
+ httpRequest.getCookie(sessionCookieName);
+ if (scookie == null) {
+ return;
+ }
+ // Override anything requested in the URL
+ if (!isRequestedSessionIdFromCookie()) {
+ // Accept only the first session id cookie
+ //scookie.getValue().convertToAscii();
+
+ setRequestedSessionId
+ (scookie.getValue().toString());
+ setRequestedSessionCookie(true);
+ setRequestedSessionURL(false);
+ } else {
+ if (!isRequestedSessionIdValid()) {
+ // Replace the session id until one is valid
+ //scookie.getValue().convertToAscii();
+ setRequestedSessionId
+ (scookie.getValue().toString());
+ }
+ }
+ }
+
+ /**
+ * Parse session id in URL.
+ */
+ protected void parseSessionId() {
+ ServletRequestImpl request = this;
+ BBuffer uriBC = httpRequest.getMsgBytes().url();
+ int semicolon = uriBC.indexOf(match, 0, match.length(), 0);
+
+ if (semicolon > 0) {
+
+ // Parse session ID, and extract it from the decoded request URI
+ int start = uriBC.getStart();
+ int end = uriBC.getEnd();
+
+ int sessionIdStart = semicolon + match.length();
+ int semicolon2 = uriBC.indexOf(';', sessionIdStart);
+ if (semicolon2 >= 0) {
+ request.setRequestedSessionId
+ (new String(uriBC.array(), start + sessionIdStart,
+ semicolon2 - sessionIdStart));
+ // Extract session ID from request URI
+ byte[] buf = uriBC.array();
+ for (int i = 0; i < end - start - semicolon2; i++) {
+ buf[start + semicolon + i]
+ = buf[start + i + semicolon2];
+ }
+ uriBC.setBytes(buf, start, end - start - semicolon2 + semicolon);
+ } else {
+ request.setRequestedSessionId
+ (new String(uriBC.array(), start + sessionIdStart,
+ (end - start) - sessionIdStart));
+ uriBC.setEnd(start + semicolon);
+ }
+ request.setRequestedSessionURL(true);
+
+ } else {
+ request.setRequestedSessionId(null);
+ request.setRequestedSessionURL(false);
+ }
+
+ }
+
+
+
+ /**
+ * Test if a given name is one of the special Servlet-spec SSL attributes.
+ */
+ static boolean isSSLAttribute(String name) {
+ return ServletRequestImpl.CERTIFICATES_ATTR.equals(name) ||
+ ServletRequestImpl.CIPHER_SUITE_ATTR.equals(name) ||
+ ServletRequestImpl.KEY_SIZE_ATTR.equals(name) ||
+ ServletRequestImpl.SSL_SESSION_ID_ATTR.equals(name);
+ }
+
+
+
+ public ServletContext getServletContext() {
+ return getContext();
+ }
+
+
+ public boolean isAsyncStarted() {
+ return httpRequest.isAsyncStarted();
+ }
+
+
+ public boolean isAsyncSupported() {
+ return false;
+ }
+
+
+ public void setAsyncTimeout(long timeout) {
+ httpRequest.setAsyncTimeout(timeout);
+ }
+
+
+ public boolean authenticate(HttpServletResponse response)
+ throws IOException, ServletException {
+ return false;
+ }
+
+ public void login(String username, String password) throws ServletException {
+ }
+
+
+ public void logout() throws ServletException {
+ }
+
+
+ public long getAsyncTimeout() {
+ return 0;
+ }
+
+}
--- /dev/null
+/*
+ * 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.servlet;
+
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpSession;
+
+import org.apache.tomcat.servlets.session.UserSessionManager;
+import org.apache.tomcat.servlets.util.Enumerator;
+import org.apache.tomcat.servlets.util.RequestUtil;
+
+
+/**
+ * Wrapper around a <code>javax.servlet.http.HttpServletRequest</code>
+ * that transforms an application request object (which might be the original
+ * one passed to a servlet, or might be based on the 2.3
+ * <code>javax.servlet.http.HttpServletRequestWrapper</code> class)
+ * back into an internal <code>org.apache.catalina.HttpRequest</code>.
+ * <p>
+ * <strong>WARNING</strong>: Due to Java's lack of support for multiple
+ * inheritance, all of the logic in <code>ApplicationRequest</code> is
+ * duplicated in <code>ApplicationHttpRequest</code>. Make sure that you
+ * keep these two classes in synchronization when making changes!
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ */
+public class ServletRequestWrapperImpl extends HttpServletRequestWrapper {
+
+
+ // ------------------------------------------------------- Static Variables
+
+
+ /**
+ * The set of attribute names that are special for request dispatchers.
+ */
+ protected static final String specials[] =
+ { RequestDispatcherImpl.INCLUDE_REQUEST_URI_ATTR,
+ RequestDispatcherImpl.INCLUDE_CONTEXT_PATH_ATTR,
+ RequestDispatcherImpl.INCLUDE_SERVLET_PATH_ATTR,
+ RequestDispatcherImpl.INCLUDE_PATH_INFO_ATTR,
+ RequestDispatcherImpl.INCLUDE_QUERY_STRING_ATTR,
+ RequestDispatcherImpl.FORWARD_REQUEST_URI_ATTR,
+ RequestDispatcherImpl.FORWARD_CONTEXT_PATH_ATTR,
+ RequestDispatcherImpl.FORWARD_SERVLET_PATH_ATTR,
+ RequestDispatcherImpl.FORWARD_PATH_INFO_ATTR,
+ RequestDispatcherImpl.FORWARD_QUERY_STRING_ATTR };
+
+
+ // ----------------------------------------------------------- Constructors
+
+
+ /**
+ * Construct a new wrapped request around the specified servlet request.
+ *
+ * @param request The servlet request being wrapped
+ */
+ public ServletRequestWrapperImpl(HttpServletRequest request,
+ ServletContextImpl context,
+ boolean crossContext) {
+
+ super(request);
+ this.context = context;
+ this.crossContext = crossContext;
+ setRequest(request);
+
+ }
+
+
+ // ----------------------------------------------------- Instance Variables
+
+
+ /**
+ * The context for this request.
+ */
+ protected ServletContextImpl context = null;
+
+
+ /**
+ * The context path for this request.
+ */
+ protected String contextPath = null;
+
+
+ /**
+ * If this request is cross context, since this changes session accesss
+ * behavior.
+ */
+ protected boolean crossContext = false;
+
+
+ /**
+ * The current dispatcher type.
+ */
+ protected Object dispatcherType = null;
+
+
+ /**
+ * Descriptive information about this implementation.
+ */
+ protected static final String info =
+ "org.apache.catalina.core.ApplicationHttpRequest/1.0";
+
+
+ /**
+ * The request parameters for this request. This is initialized from the
+ * wrapped request, but updates are allowed.
+ */
+ protected Map parameters = null;
+
+
+ /**
+ * Have the parameters for this request already been parsed?
+ */
+ private boolean parsedParams = false;
+
+
+ /**
+ * The path information for this request.
+ */
+ protected String pathInfo = null;
+
+
+ /**
+ * The query parameters for the current request.
+ */
+ private String queryParamString = null;
+
+
+ /**
+ * The query string for this request.
+ */
+ protected String queryString = null;
+
+
+ /**
+ * The current request dispatcher path.
+ */
+ protected Object requestDispatcherPath = null;
+
+
+ /**
+ * The request URI for this request.
+ */
+ protected String requestURI = null;
+
+
+ /**
+ * The servlet path for this request.
+ */
+ protected String servletPath = null;
+
+
+ /**
+ * The currently active session for this request.
+ */
+ protected HttpSession session = null;
+
+
+ /**
+ * Special attributes.
+ */
+ protected Object[] specialAttributes = new Object[specials.length];
+
+
+ // ------------------------------------------------- ServletRequest Methods
+
+
+ /**
+ * Override the <code>getAttribute()</code> method of the wrapped request.
+ *
+ * @param name Name of the attribute to retrieve
+ */
+ public Object getAttribute(String name) {
+
+ if (name.equals(ServletRequestImpl.DISPATCHER_TYPE_ATTR)) {
+ return dispatcherType;
+ } else if (name.equals(ServletRequestImpl.DISPATCHER_REQUEST_PATH_ATTR)) {
+ if ( requestDispatcherPath != null ){
+ return requestDispatcherPath.toString();
+ } else {
+ return null;
+ }
+ }
+
+ int pos = getSpecial(name);
+ if (pos == -1) {
+ return getRequest().getAttribute(name);
+ } else {
+ if ((specialAttributes[pos] == null)
+ && (specialAttributes[5] == null) && (pos >= 5)) {
+ // If it's a forward special attribute, and null, it means this
+ // is an include, so we check the wrapped request since
+ // the request could have been forwarded before the include
+ return getRequest().getAttribute(name);
+ } else {
+ return specialAttributes[pos];
+ }
+ }
+
+ }
+
+
+ /**
+ * Override the <code>getAttributeNames()</code> method of the wrapped
+ * request.
+ */
+ public Enumeration getAttributeNames() {
+ return (new AttributeNamesEnumerator());
+ }
+
+
+ /**
+ * Override the <code>removeAttribute()</code> method of the
+ * wrapped request.
+ *
+ * @param name Name of the attribute to remove
+ */
+ public void removeAttribute(String name) {
+
+ if (!removeSpecial(name))
+ getRequest().removeAttribute(name);
+
+ }
+
+
+ /**
+ * Override the <code>setAttribute()</code> method of the
+ * wrapped request.
+ *
+ * @param name Name of the attribute to set
+ * @param value Value of the attribute to set
+ */
+ public void setAttribute(String name, Object value) {
+
+ if (name.equals(ServletRequestImpl.DISPATCHER_TYPE_ATTR)) {
+ dispatcherType = value;
+ return;
+ } else if (name.equals(ServletRequestImpl.DISPATCHER_REQUEST_PATH_ATTR)) {
+ requestDispatcherPath = value;
+ return;
+ }
+
+ if (!setSpecial(name, value)) {
+ getRequest().setAttribute(name, value);
+ }
+
+ }
+
+
+ /**
+ * Return a RequestDispatcher that wraps the resource at the specified
+ * path, which may be interpreted as relative to the current request path.
+ *
+ * @param path Path of the resource to be wrapped
+ */
+ public RequestDispatcher getRequestDispatcher(String path) {
+
+ if (context == null)
+ return (null);
+
+ // If the path is already context-relative, just pass it through
+ if (path == null)
+ return (null);
+ else if (path.startsWith("/"))
+ return (context.getServletContext().getRequestDispatcher(path));
+
+ // Convert a request-relative path to a context-relative one
+ String servletPath =
+ (String) getAttribute(RequestDispatcherImpl.INCLUDE_SERVLET_PATH_ATTR);
+ if (servletPath == null)
+ servletPath = getServletPath();
+
+ // Add the path info, if there is any
+ String pathInfo = getPathInfo();
+ String requestPath = null;
+
+ if (pathInfo == null) {
+ requestPath = servletPath;
+ } else {
+ requestPath = servletPath + pathInfo;
+ }
+
+ int pos = requestPath.lastIndexOf('/');
+ String relative = null;
+ if (pos >= 0) {
+ relative = RequestUtil.normalize
+ (requestPath.substring(0, pos + 1) + path);
+ } else {
+ relative = RequestUtil.normalize(requestPath + path);
+ }
+
+ return (context.getServletContext().getRequestDispatcher(relative));
+
+ }
+
+
+ // --------------------------------------------- HttpServletRequest Methods
+
+
+ /**
+ * Override the <code>getContextPath()</code> method of the wrapped
+ * request.
+ */
+ public String getContextPath() {
+
+ return (this.contextPath);
+
+ }
+
+
+ /**
+ * Override the <code>getParameter()</code> method of the wrapped request.
+ *
+ * @param name Name of the requested parameter
+ */
+ public String getParameter(String name) {
+
+ parseParameters();
+
+ Object value = parameters.get(name);
+ if (value == null)
+ return (null);
+ else if (value instanceof String[])
+ return (((String[]) value)[0]);
+ else if (value instanceof String)
+ return ((String) value);
+ else
+ return (value.toString());
+
+ }
+
+
+ /**
+ * Override the <code>getParameterMap()</code> method of the
+ * wrapped request.
+ */
+ public Map getParameterMap() {
+
+ parseParameters();
+ return (parameters);
+
+ }
+
+
+ /**
+ * Override the <code>getParameterNames()</code> method of the
+ * wrapped request.
+ */
+ public Enumeration getParameterNames() {
+
+ parseParameters();
+ return (new Enumerator(parameters.keySet()));
+
+ }
+
+
+ /**
+ * Override the <code>getParameterValues()</code> method of the
+ * wrapped request.
+ *
+ * @param name Name of the requested parameter
+ */
+ public String[] getParameterValues(String name) {
+
+ parseParameters();
+ Object value = parameters.get(name);
+ if (value == null)
+ return ((String[]) null);
+ else if (value instanceof String[])
+ return ((String[]) value);
+ else if (value instanceof String) {
+ String values[] = new String[1];
+ values[0] = (String) value;
+ return (values);
+ } else {
+ String values[] = new String[1];
+ values[0] = value.toString();
+ return (values);
+ }
+
+ }
+
+
+ /**
+ * Override the <code>getPathInfo()</code> method of the wrapped request.
+ */
+ public String getPathInfo() {
+
+ return (this.pathInfo);
+
+ }
+
+
+ /**
+ * Override the <code>getQueryString()</code> method of the wrapped
+ * request.
+ */
+ public String getQueryString() {
+
+ return (this.queryString);
+
+ }
+
+
+ /**
+ * Override the <code>getRequestURI()</code> method of the wrapped
+ * request.
+ */
+ public String getRequestURI() {
+
+ return (this.requestURI);
+
+ }
+
+
+ /**
+ * Override the <code>getRequestURL()</code> method of the wrapped
+ * request.
+ */
+ public StringBuffer getRequestURL() {
+
+ StringBuffer url = new StringBuffer();
+ String scheme = getScheme();
+ int port = getServerPort();
+ if (port < 0)
+ port = 80; // Work around java.net.URL bug
+
+ url.append(scheme);
+ url.append("://");
+ url.append(getServerName());
+ if ((scheme.equals("http") && (port != 80))
+ || (scheme.equals("https") && (port != 443))) {
+ url.append(':');
+ url.append(port);
+ }
+ url.append(getRequestURI());
+
+ return (url);
+
+ }
+
+
+ /**
+ * Override the <code>getServletPath()</code> method of the wrapped
+ * request.
+ */
+ public String getServletPath() {
+
+ return (this.servletPath);
+
+ }
+
+
+ /**
+ * Return the session associated with this Request, creating one
+ * if necessary.
+ */
+ public HttpSession getSession() {
+ return (getSession(true));
+ }
+
+
+ /**
+ * Return the session associated with this Request, creating one
+ * if necessary and requested.
+ *
+ * @param create Create a new session if one does not exist
+ */
+ public HttpSession getSession(boolean create) {
+
+ if (crossContext) {
+
+ // There cannot be a session if no context has been assigned yet
+ if (context == null)
+ return (null);
+ UserSessionManager manager = context.getManager();
+ // Return the current session if it exists and is valid
+ if (session != null && manager.isValid(session)) {
+ return session;
+ }
+
+ HttpSession other = super.getSession(false);
+ if (create && (other == null)) {
+ // First create a session in the first context: the problem is
+ // that the top level request is the only one which can
+ // create the cookie safely
+ other = super.getSession(true);
+ }
+ if (other != null) {
+ HttpSession localSession = null;
+ try {
+ localSession =
+ manager.findSession(other.getId());
+ } catch (IOException e) {
+ // Ignore
+ }
+ if (localSession == null && create) {
+ localSession =
+ context.getManager().createSession(other.getId());
+ }
+ if (localSession != null) {
+ context.getManager().access(localSession);
+ session = localSession;
+ return session;
+ }
+ }
+ return null;
+
+ } else {
+ return super.getSession(create);
+ }
+
+ }
+
+
+ /**
+ * Returns true if the request specifies a JSESSIONID that is valid within
+ * the context of this ApplicationHttpRequest, false otherwise.
+ *
+ * @return true if the request specifies a JSESSIONID that is valid within
+ * the context of this ApplicationHttpRequest, false otherwise.
+ */
+ public boolean isRequestedSessionIdValid() {
+
+ if (crossContext) {
+
+ String requestedSessionId = getRequestedSessionId();
+ if (requestedSessionId == null)
+ return (false);
+ if (context == null)
+ return (false);
+ UserSessionManager manager = context.getManager();
+ if (manager == null)
+ return (false);
+ HttpSession session = null;
+ try {
+ session = manager.findSession(requestedSessionId);
+ } catch (IOException e) {
+ session = null;
+ }
+ if ((session != null) && manager.isValid(session)) {
+ return (true);
+ } else {
+ return (false);
+ }
+
+ } else {
+ return super.isRequestedSessionIdValid();
+ }
+ }
+
+
+ // -------------------------------------------------------- Package Methods
+
+
+ /**
+ * Recycle this request
+ */
+ public void recycle() {
+ if (session != null) {
+ context.getManager().endAccess(session);
+ }
+ }
+
+
+ /**
+ * Return descriptive information about this implementation.
+ */
+ public String getInfo() {
+
+ return (info);
+
+ }
+
+
+ /**
+ * Perform a shallow copy of the specified Map, and return the result.
+ *
+ * @param orig Origin Map to be copied
+ */
+ Map copyMap(Map orig) {
+
+ if (orig == null)
+ return (new HashMap());
+ HashMap dest = new HashMap();
+ Iterator keys = orig.keySet().iterator();
+ while (keys.hasNext()) {
+ String key = (String) keys.next();
+ dest.put(key, orig.get(key));
+ }
+ return (dest);
+
+ }
+
+
+ /**
+ * Set the context path for this request.
+ *
+ * @param contextPath The new context path
+ */
+ void setContextPath(String contextPath) {
+
+ this.contextPath = contextPath;
+
+ }
+
+
+ /**
+ * Set the path information for this request.
+ *
+ * @param pathInfo The new path info
+ */
+ void setPathInfo(String pathInfo) {
+
+ this.pathInfo = pathInfo;
+
+ }
+
+
+ /**
+ * Set the query string for this request.
+ *
+ * @param queryString The new query string
+ */
+ void setQueryString(String queryString) {
+
+ this.queryString = queryString;
+
+ }
+
+
+ /**
+ * Set the request that we are wrapping.
+ *
+ * @param request The new wrapped request
+ */
+ void setRequest(HttpServletRequest request) {
+
+ super.setRequest(request);
+
+ // Initialize the attributes for this request
+ dispatcherType = request.getAttribute(ServletRequestImpl.DISPATCHER_TYPE_ATTR);
+ requestDispatcherPath =
+ request.getAttribute(ServletRequestImpl.DISPATCHER_REQUEST_PATH_ATTR);
+
+ // Initialize the path elements for this request
+ contextPath = request.getContextPath();
+ pathInfo = request.getPathInfo();
+ queryString = request.getQueryString();
+ requestURI = request.getRequestURI();
+ servletPath = request.getServletPath();
+
+ }
+
+
+ /**
+ * Set the request URI for this request.
+ *
+ * @param requestURI The new request URI
+ */
+ void setRequestURI(String requestURI) {
+
+ this.requestURI = requestURI;
+
+ }
+
+
+ /**
+ * Set the servlet path for this request.
+ *
+ * @param servletPath The new servlet path
+ */
+ void setServletPath(String servletPath) {
+
+ this.servletPath = servletPath;
+
+ }
+
+
+ /**
+ * Parses the parameters of this request.
+ *
+ * If parameters are present in both the query string and the request
+ * content, they are merged.
+ */
+ void parseParameters() {
+
+ if (parsedParams) {
+ return;
+ }
+
+ parameters = new HashMap();
+ parameters = copyMap(getRequest().getParameterMap());
+ mergeParameters();
+ parsedParams = true;
+ }
+
+
+ /**
+ * Save query parameters for this request.
+ *
+ * @param queryString The query string containing parameters for this
+ * request
+ */
+ void setQueryParams(String queryString) {
+ this.queryParamString = queryString;
+ }
+
+
+ // ------------------------------------------------------ Protected Methods
+
+
+ /**
+ * Is this attribute name one of the special ones that is added only for
+ * included servlets?
+ *
+ * @param name Attribute name to be tested
+ */
+ protected boolean isSpecial(String name) {
+
+ for (int i = 0; i < specials.length; i++) {
+ if (specials[i].equals(name))
+ return (true);
+ }
+ return (false);
+
+ }
+
+
+ /**
+ * Get a special attribute.
+ *
+ * @return the special attribute pos, or -1 if it is not a special
+ * attribute
+ */
+ protected int getSpecial(String name) {
+ for (int i = 0; i < specials.length; i++) {
+ if (specials[i].equals(name)) {
+ return (i);
+ }
+ }
+ return (-1);
+ }
+
+
+ /**
+ * Set a special attribute.
+ *
+ * @return true if the attribute was a special attribute, false otherwise
+ */
+ protected boolean setSpecial(String name, Object value) {
+ for (int i = 0; i < specials.length; i++) {
+ if (specials[i].equals(name)) {
+ specialAttributes[i] = value;
+ return (true);
+ }
+ }
+ return (false);
+ }
+
+
+ /**
+ * Remove a special attribute.
+ *
+ * @return true if the attribute was a special attribute, false otherwise
+ */
+ protected boolean removeSpecial(String name) {
+ for (int i = 0; i < specials.length; i++) {
+ if (specials[i].equals(name)) {
+ specialAttributes[i] = null;
+ return (true);
+ }
+ }
+ return (false);
+ }
+
+
+ /**
+ * Merge the two sets of parameter values into a single String array.
+ *
+ * @param values1 First set of values
+ * @param values2 Second set of values
+ */
+ protected String[] mergeValues(Object values1, Object values2) {
+
+ ArrayList results = new ArrayList();
+
+ if (values1 == null)
+ ;
+ else if (values1 instanceof String)
+ results.add(values1);
+ else if (values1 instanceof String[]) {
+ String values[] = (String[]) values1;
+ for (int i = 0; i < values.length; i++)
+ results.add(values[i]);
+ } else
+ results.add(values1.toString());
+
+ if (values2 == null)
+ ;
+ else if (values2 instanceof String)
+ results.add(values2);
+ else if (values2 instanceof String[]) {
+ String values[] = (String[]) values2;
+ for (int i = 0; i < values.length; i++)
+ results.add(values[i]);
+ } else
+ results.add(values2.toString());
+
+ String values[] = new String[results.size()];
+ return ((String[]) results.toArray(values));
+
+ }
+
+
+ // ------------------------------------------------------ Private Methods
+
+
+ /**
+ * Merge the parameters from the saved query parameter string (if any), and
+ * the parameters already present on this request (if any), such that the
+ * parameter values from the query string show up first if there are
+ * duplicate parameter names.
+ */
+ private void mergeParameters() {
+
+ if ((queryParamString == null) || (queryParamString.length() < 1))
+ return;
+
+ HashMap queryParameters = new HashMap();
+ String encoding = getCharacterEncoding();
+ if (encoding == null)
+ encoding = "ISO-8859-1";
+ try {
+ RequestUtil.parseParameters
+ (queryParameters, queryParamString, encoding);
+ } catch (Exception e) {
+ ;
+ }
+ Iterator keys = parameters.keySet().iterator();
+ while (keys.hasNext()) {
+ String key = (String) keys.next();
+ Object value = queryParameters.get(key);
+ if (value == null) {
+ queryParameters.put(key, parameters.get(key));
+ continue;
+ }
+ queryParameters.put
+ (key, mergeValues(value, parameters.get(key)));
+ }
+ parameters = queryParameters;
+
+ }
+
+
+ // ----------------------------------- AttributeNamesEnumerator Inner Class
+
+
+ /**
+ * Utility class used to expose the special attributes as being available
+ * as request attributes.
+ */
+ protected class AttributeNamesEnumerator implements Enumeration {
+
+ protected int pos = -1;
+ protected int last = -1;
+ protected Enumeration parentEnumeration = null;
+ protected String next = null;
+
+ public AttributeNamesEnumerator() {
+ parentEnumeration = getRequest().getAttributeNames();
+ for (int i = 0; i < specialAttributes.length; i++) {
+ if (getAttribute(specials[i]) != null) {
+ last = i;
+ }
+ }
+ }
+
+ public boolean hasMoreElements() {
+ return ((pos != last) || (next != null)
+ || ((next = findNext()) != null));
+ }
+
+ public Object nextElement() {
+ if (pos != last) {
+ for (int i = pos + 1; i <= last; i++) {
+ if (getAttribute(specials[i]) != null) {
+ pos = i;
+ return (specials[i]);
+ }
+ }
+ }
+ String result = next;
+ if (next != null) {
+ next = findNext();
+ } else {
+ throw new NoSuchElementException();
+ }
+ return result;
+ }
+
+ protected String findNext() {
+ String result = null;
+ while ((result == null) && (parentEnumeration.hasMoreElements())) {
+ String current = (String) parentEnumeration.nextElement();
+ if (!isSpecial(current)) {
+ result = current;
+ }
+ }
+ return result;
+ }
+
+ }
+
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.servlet;
+
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.util.Vector;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.apache.tomcat.lite.http.HttpResponse;
+import org.apache.tomcat.lite.http.HttpWriter;
+import org.apache.tomcat.lite.http.MultiMap;
+import org.apache.tomcat.lite.http.ServerCookie;
+import org.apache.tomcat.lite.http.MultiMap.Entry;
+import org.apache.tomcat.lite.io.CBuffer;
+import org.apache.tomcat.lite.io.FastHttpDateFormat;
+import org.apache.tomcat.lite.io.IOWriter;
+
+/**
+ * Wrapper object for the Coyote response.
+ *
+ * @author Remy Maucherat
+ * @author Craig R. McClanahan
+ * @version $Revision: 793797 $ $Date: 2009-07-13 22:38:02 -0700 (Mon, 13 Jul 2009) $
+ */
+
+public class ServletResponseImpl
+ implements HttpServletResponse {
+
+ /**
+ * Format for http response header date field
+ * From DateTool
+ */
+ public static final String HTTP_RESPONSE_DATE_HEADER =
+ "EEE, dd MMM yyyy HH:mm:ss zzz";
+
+ // ----------------------------------------------------------- Constructors
+
+
+ // Package - only ServletRequestImpl should call it.
+ ServletResponseImpl() {
+ }
+
+
+ /**
+ * The date format we will use for creating date headers.
+ */
+ protected SimpleDateFormat format = null;
+
+
+ /**
+ * The associated output buffer.
+ */
+ protected HttpWriter outputBuffer;
+
+
+ /**
+ * The associated output stream.
+ */
+ protected ServletOutputStreamImpl outputStream;
+
+
+ /**
+ * The associated writer.
+ */
+ protected PrintWriter writer;
+
+
+ /**
+ * The application commit flag.
+ */
+ protected boolean appCommitted = false;
+
+
+ /**
+ * The included flag.
+ */
+ protected boolean included = false;
+
+
+ /**
+ * The characterEncoding flag
+ */
+ private boolean isCharacterEncodingSet = false;
+
+ /**
+ * The error flag.
+ */
+ protected boolean error = false;
+
+
+ /**
+ * The set of Cookies associated with this Response.
+ * Only used in facade - base object uses ServerCookie
+ */
+ protected ArrayList<Cookie> cookies = new ArrayList<Cookie>();
+
+
+ /**
+ * Using output stream flag.
+ */
+ protected boolean usingOutputStream = false;
+
+
+ /**
+ * Using writer flag.
+ */
+ protected boolean usingWriter = false;
+
+
+ protected ServletRequestImpl req = null;
+
+
+ protected CBuffer tmpUrlBuffer = CBuffer.newInstance();
+
+ private HttpResponse resB;
+
+
+ // Cached/derived information - reflected in headers
+ protected static Locale DEFAULT_LOCALE = Locale.getDefault();
+
+ public static final String DEFAULT_CHARACTER_ENCODING="ISO-8859-1";
+
+ protected Locale locale = DEFAULT_LOCALE;
+
+ // XXX
+ protected boolean commited = false;
+ protected String contentType = null;
+
+ /**
+ * Has the charset been explicitly set.
+ */
+ protected boolean charsetSet = false;
+ protected String characterEncoding = DEFAULT_CHARACTER_ENCODING;
+
+
+ // --------------------------------------------------------- Public Methods
+
+ /**
+ * Release all object references, and initialize instance variables, in
+ * preparation for reuse of this object.
+ */
+ void recycle() {
+
+ usingOutputStream = false;
+ usingWriter = false;
+ appCommitted = false;
+ commited = false;
+ included = false;
+ error = false;
+ isCharacterEncodingSet = false;
+
+ cookies.clear();
+
+ outputBuffer.recycle();
+
+ //resB.recycle();
+ }
+
+
+ // ------------------------------------------------------- Response Methods
+
+
+ /**
+ * Return the number of bytes actually written to the output stream.
+ */
+// public int getContentCount() {
+// return outputBuffer.getBytesWritten() + outputBuffer.getCharsWritten();
+// }
+
+
+ /**
+ * Set the application commit flag.
+ *
+ * @param appCommitted The new application committed flag value
+ */
+ public void setAppCommitted(boolean appCommitted) {
+ this.appCommitted = appCommitted;
+ }
+
+
+// /**
+// * Application commit flag accessor.
+// */
+// public boolean isAppCommitted() {
+// return (this.appCommitted || isCommitted() || isSuspended()
+// || ((getHttpResponse().getContentLength() > 0)
+// && (getContentCount() >= getHttpResponse().getContentLength())));
+// }
+
+
+ /**
+ * Return the "processing inside an include" flag.
+ */
+ public boolean getIncluded() {
+ return included;
+ }
+
+
+ /**
+ * Set the "processing inside an include" flag.
+ *
+ * @param included <code>true</code> if we are currently inside a
+ * RequestDispatcher.include(), else <code>false</code>
+ */
+ public void setIncluded(boolean included) {
+ this.included = included;
+ }
+
+
+ /**
+ * Return the Request with which this Response is associated.
+ */
+ public ServletRequestImpl getRequest() {
+ return (this.req);
+ }
+
+ /**
+ * Set the Request with which this Response is associated.
+ *
+ * @param request The new associated request
+ */
+ public void setRequest(ServletRequestImpl request) {
+ this.req = (ServletRequestImpl) request;
+ }
+
+
+ /**
+ * Return the output stream associated with this Response.
+ */
+ public OutputStream getStream() {
+ return outputStream;
+ }
+
+
+ /**
+ * Set the output stream associated with this Response.
+ *
+ * @param stream The new output stream
+ */
+ public void setStream(OutputStream stream) {
+ // This method is evil
+ }
+
+
+ /**
+ * Set the suspended flag.
+ *
+ * @param suspended The new suspended flag value
+ */
+ public void setSuspended(boolean suspended) throws IOException {
+ //coyoteResponse.setCommitted(true);
+ flushBuffer();
+ outputBuffer.setSuspended(suspended);
+ }
+
+
+ /**
+ * Suspended flag accessor.
+ */
+ public boolean isSuspended() {
+ return outputBuffer.isSuspended();
+ }
+
+
+ /**
+ * Set the error flag.
+ */
+ public void setError() {
+ error = true;
+ }
+
+
+ /**
+ * Error flag accessor.
+ */
+ public boolean isError() {
+ return error;
+ }
+
+
+ /**
+ * Create and return a ServletOutputStream to write the content
+ * associated with this Response.
+ *
+ * @exception IOException if an input/output error occurs
+ */
+ public ServletOutputStream createOutputStream()
+ throws IOException {
+ // Probably useless
+ return outputStream;
+ }
+
+ /**
+ * Return the content type that was set or calculated for this response,
+ * or <code>null</code> if no content type was set.
+ */
+ public String getContentType() {
+ String ret = contentType;
+
+ if (ret != null
+ && characterEncoding != null
+ && charsetSet) {
+ ret = ret + ";charset=" + characterEncoding;
+ }
+
+ return ret;
+ }
+
+
+ // ------------------------------------------------ ServletResponse Methods
+
+
+ /**
+ * Flush the buffer and commit this response.
+ *
+ * @exception IOException if an input/output error occurs
+ */
+ public void flushBuffer()
+ throws IOException {
+ outputBuffer.flush();
+ }
+
+
+ /**
+ * Return the actual buffer size used for this Response.
+ */
+ public int getBufferSize() {
+ return outputBuffer.getBufferSize();
+ }
+
+
+ /**
+ * Return the character encoding used for this Response.
+ */
+ public String getCharacterEncoding() {
+ return characterEncoding;
+ }
+
+
+ /**
+ * Return the servlet output stream associated with this Response.
+ *
+ * @exception IllegalStateException if <code>getWriter</code> has
+ * already been called for this response
+ * @exception IOException if an input/output error occurs
+ */
+ public ServletOutputStream getOutputStream()
+ throws IOException {
+
+ if (usingWriter)
+ throw new IllegalStateException
+ ("usingWriter");
+
+ usingOutputStream = true;
+ return outputStream;
+
+ }
+
+ public HttpWriter getOutputBuffer() {
+ return outputBuffer;
+ }
+
+
+ /**
+ * Return the Locale assigned to this response.
+ */
+ public Locale getLocale() {
+ return locale;
+ }
+
+
+ /**
+ * Return the writer associated with this Response.
+ *
+ * @exception IllegalStateException if <code>getOutputStream</code> has
+ * already been called for this response
+ * @exception IOException if an input/output error occurs
+ */
+ public PrintWriter getWriter()
+ throws IOException {
+
+ if (usingOutputStream)
+ throw new IllegalStateException
+ ("usingOutputStream");
+
+ /*
+ * If the response's character encoding has not been specified as
+ * described in <code>getCharacterEncoding</code> (i.e., the method
+ * just returns the default value <code>ISO-8859-1</code>),
+ * <code>getWriter</code> updates it to <code>ISO-8859-1</code>
+ * (with the effect that a subsequent call to getContentType() will
+ * include a charset=ISO-8859-1 component which will also be
+ * reflected in the Content-Type response header, thereby satisfying
+ * the Servlet spec requirement that containers must communicate the
+ * character encoding used for the servlet response's writer to the
+ * client).
+ */
+ setCharacterEncoding(getCharacterEncoding());
+
+ usingWriter = true;
+ // Otherwise it'll be set on first write - encoding
+ // should be fixed.
+ outputBuffer.checkConverter();
+
+ if (writer == null) {
+ writer = new ServletWriterImpl(outputBuffer);
+ }
+ return writer;
+
+ }
+
+
+ /**
+ * Has the output of this response already been committed?
+ */
+ public boolean isCommitted() {
+ return getHttpResponse().isCommitted();
+ }
+
+ /**
+ * Clear any content written to the buffer.
+ *
+ * @exception IllegalStateException if this response has already
+ * been committed
+ */
+ public void reset() {
+
+ if (included)
+ return; // Ignore any call from an included servlet
+
+ if (isCommitted())
+ throw new IllegalStateException("isCommitted");
+
+ resB.recycle(); // reset headers, status code, message
+ //req.getConnector().reset(this);
+ contentType = null;
+ locale = DEFAULT_LOCALE;
+ characterEncoding = DEFAULT_CHARACTER_ENCODING;
+ charsetSet = false;
+
+ outputBuffer.reset();
+ }
+
+
+ /**
+ * Reset the data buffer but not any status or header information.
+ *
+ * @exception IllegalStateException if the response has already
+ * been committed
+ */
+ public void resetBuffer() {
+
+ if (isCommitted())
+ throw new IllegalStateException("isCommitted");
+
+ outputBuffer.reset();
+
+ }
+
+
+ /**
+ * Set the buffer size to be used for this Response.
+ *
+ * @param size The new buffer size
+ *
+ * @exception IllegalStateException if this method is called after
+ * output has been committed for this response
+ */
+ public void setBufferSize(int size) {
+
+ if (isCommitted() || outputBuffer.getWrittenSinceFlush() > 0 ||
+ getHttpResponse().getBodyOutputStream().getWrittenSinceFlush() > 0)
+ throw new IllegalStateException
+ ("isCommitted || !isNew");
+
+ outputBuffer.setBufferSize(size);
+
+ }
+
+
+ /**
+ * Set the content length (in bytes) for this Response.
+ * Ignored for writers if non-ISO-8859-1 encoding ( we could add more
+ * encodings that are constant.
+ */
+ public void setContentLength(int length) {
+
+ if (isCommitted())
+ return;
+
+ // Ignore any call from an included servlet
+ if (included)
+ return;
+
+ // writers can use variable-length encoding.
+ if (usingWriter && !"ISO-8859-1".equals(getCharacterEncoding())) {
+ return;
+ }
+ getHttpResponse().setContentLength(length);
+
+ }
+
+
+ /**
+ * Set the content type for this Response.
+ *
+ * @param type The new content type
+ */
+ public void setContentType(String type) {
+
+ if (isCommitted())
+ return;
+
+ // Ignore any call from an included servlet
+ if (included)
+ return;
+
+ // Ignore charset if getWriter() has already been called
+ if (usingWriter) {
+ if (type != null) {
+ int index = type.indexOf(";");
+ if (index != -1) {
+ type = type.substring(0, index);
+ }
+ }
+ }
+
+ getHttpResponse().setContentType(type);
+
+ // Check to see if content type contains charset
+ if (type != null) {
+ int index = type.indexOf(";");
+ if (index != -1) {
+ int len = type.length();
+ index++;
+ while (index < len && Character.isSpace(type.charAt(index))) {
+ index++;
+ }
+ if (index+7 < len
+ && type.charAt(index) == 'c'
+ && type.charAt(index+1) == 'h'
+ && type.charAt(index+2) == 'a'
+ && type.charAt(index+3) == 'r'
+ && type.charAt(index+4) == 's'
+ && type.charAt(index+5) == 'e'
+ && type.charAt(index+6) == 't'
+ && type.charAt(index+7) == '=') {
+ isCharacterEncodingSet = true;
+ }
+ }
+ }
+ }
+
+
+ /*
+ * Overrides the name of the character encoding used in the body
+ * of the request. This method must be called prior to reading
+ * request parameters or reading input using getReader().
+ *
+ * @param charset String containing the name of the chararacter encoding.
+ */
+ public void setCharacterEncoding(String charset) {
+
+ if (isCommitted())
+ return;
+
+ // Ignore any call from an included servlet
+ if (included)
+ return;
+
+ // Ignore any call made after the getWriter has been invoked
+ // The default should be used
+ if (usingWriter)
+ return;
+
+ if (isCommitted())
+ return;
+ if (charset == null)
+ return;
+
+ characterEncoding = charset;
+ charsetSet=true;
+ isCharacterEncodingSet = true;
+ }
+
+
+
+ /**
+ * Set the Locale that is appropriate for this response, including
+ * setting the appropriate character encoding.
+ *
+ * @param locale The new locale
+ */
+ public void setLocale(Locale locale) {
+
+ if (isCommitted())
+ return;
+
+ // Ignore any call from an included servlet
+ if (included)
+ return;
+
+ if (locale == null) {
+ return; // throw an exception?
+ }
+
+ // Save the locale for use by getLocale()
+ this.locale = locale;
+
+ // Set the contentLanguage for header output
+ String contentLanguage = locale.getLanguage();
+ if ((contentLanguage != null) && (contentLanguage.length() > 0)) {
+ String country = locale.getCountry();
+ StringBuffer value = new StringBuffer(contentLanguage);
+ if ((country != null) && (country.length() > 0)) {
+ value.append('-');
+ value.append(country);
+ }
+ contentLanguage = value.toString();
+ }
+ resB.setHeader("Content-Language", contentLanguage);
+
+ // Ignore any call made after the getWriter has been invoked.
+ // The default should be used
+ if (usingWriter)
+ return;
+
+ if (isCharacterEncodingSet) {
+ return;
+ }
+
+ Locale2Charset cm = req.getContext().getCharsetMapper();
+ String charset = cm.getCharset( locale );
+ if ( charset != null ){
+ setCharacterEncoding(charset);
+ }
+
+ }
+
+
+ // --------------------------------------------------- HttpResponse Methods
+
+
+ /**
+ * Return an array of all cookies set for this response, or
+ * a zero-length array if no cookies have been set.
+ */
+ public Cookie[] getCookies() {
+ return ((Cookie[]) cookies.toArray(new Cookie[cookies.size()]));
+ }
+
+
+ /**
+ * Return the value for the specified header, or <code>null</code> if this
+ * header has not been set. If more than one value was added for this
+ * name, only the first is returned; use getHeaderValues() to retrieve all
+ * of them.
+ *
+ * @param name Header name to look up
+ */
+ public String getHeader(String name) {
+ return getHttpResponse().getHeader(name);
+ }
+
+
+ /**
+ * Return an array of all the header names set for this response, or
+ * a zero-length array if no headers have been set.
+ */
+ public Collection<String> getHeaderNames() {
+ return getHttpResponse().getHeaderNames();
+ }
+
+ public Collection<String> getHeaders(String name) {
+ return null;
+ }
+
+ /**
+ * Return an array of all the header values associated with the
+ * specified header name, or an zero-length array if there are no such
+ * header values.
+ *
+ * @param name Header name to look up
+ */
+ public String[] getHeaderValues(String name) {
+ Entry entry = getHttpResponse().getMimeHeaders().getEntry(name);
+ if (entry == null) {
+ return new String[] {};
+ }
+ int size = entry.values.size();
+ String[] resultArray = new String[size];
+ for (int i = 0; i < size; i++) {
+ resultArray[i] = entry.values.get(i).getValue().toString();
+ }
+ return resultArray;
+
+ }
+
+
+ /**
+ * Return the error message that was set with <code>sendError()</code>
+ * for this Response.
+ */
+ public String getMessage() {
+ return getHttpResponse().getMessage();
+ }
+
+
+ /**
+ * Return the HTTP status code associated with this Response.
+ */
+ public int getStatus() {
+ return getHttpResponse().getStatus();
+ }
+
+
+ /**
+ * Reset this response, and specify the values for the HTTP status code
+ * and corresponding message.
+ *
+ * @exception IllegalStateException if this response has already been
+ * committed
+ */
+ public void reset(int status, String message) {
+ reset();
+ setStatus(status, message);
+ }
+
+
+ // -------------------------------------------- HttpServletResponse Methods
+
+
+ /**
+ * Add the specified Cookie to those that will be included with
+ * this Response.
+ *
+ * @param cookie Cookie to be added
+ */
+ public void addCookie(final Cookie cookie) {
+
+ if (isCommitted())
+ return;
+
+ // Ignore any call from an included servlet
+ if (included)
+ return;
+
+ cookies.add(cookie);
+
+ final StringBuffer sb = new StringBuffer();
+ ServerCookie.appendCookieValue
+ (sb, cookie.getVersion(), cookie.getName(), cookie.getValue(),
+ cookie.getPath(), cookie.getDomain(), cookie.getComment(),
+ cookie.getMaxAge(), cookie.getSecure(), false);
+
+ // the header name is Set-Cookie for both "old" and v.1 ( RFC2109 )
+ // RFC2965 is not supported by browsers and the Servlet spec
+ // asks for 2109.
+ addHeader("Set-Cookie", sb.toString());
+
+ }
+
+
+ /**
+ * Add the specified date header to the specified value.
+ *
+ * @param name Name of the header to set
+ * @param value Date value to be set
+ */
+ public void addDateHeader(String name, long value) {
+
+ if (isCommitted())
+ return;
+
+ // Ignore any call from an included servlet
+ if (included) {
+ return;
+ }
+
+ if (format == null) {
+ format = new SimpleDateFormat(HTTP_RESPONSE_DATE_HEADER,
+ Locale.US);
+ format.setTimeZone(TimeZone.getTimeZone("GMT"));
+ }
+
+ addHeader(name, FastHttpDateFormat.formatDate(value, format));
+
+ }
+
+
+ /**
+ * Add the specified header to the specified value.
+ *
+ * @param name Name of the header to set
+ * @param value Value to be set
+ */
+ public void addHeader(String name, String value) {
+
+ if (isCommitted())
+ return;
+
+ // Ignore any call from an included servlet
+ if (included)
+ return;
+
+ getHttpResponse().addHeader(name, value);
+
+ }
+
+
+ /**
+ * Add the specified integer header to the specified value.
+ *
+ * @param name Name of the header to set
+ * @param value Integer value to be set
+ */
+ public void addIntHeader(String name, int value) {
+
+ if (isCommitted())
+ return;
+
+ // Ignore any call from an included servlet
+ if (included)
+ return;
+
+ addHeader(name, "" + value);
+
+ }
+
+
+ /**
+ * Has the specified header been set already in this response?
+ *
+ * @param name Name of the header to check
+ */
+ public boolean containsHeader(String name) {
+ // Need special handling for Content-Type and Content-Length due to
+ // special handling of these in coyoteResponse
+ char cc=name.charAt(0);
+ if(cc=='C' || cc=='c') {
+ if(name.equalsIgnoreCase("Content-Type")) {
+ // Will return null if this has not been set
+ return getContentType() != null;
+ }
+ if(name.equalsIgnoreCase("Content-Length")) {
+ // -1 means not known and is not sent to client
+ return (getHttpResponse().getContentLength() != -1);
+ }
+ }
+
+ return getHttpResponse().containsHeader(name);
+ }
+
+
+ /**
+ * Encode the session identifier associated with this response
+ * into the specified redirect URL, if necessary.
+ *
+ * @param url URL to be encoded
+ */
+ public String encodeRedirectURL(String url) {
+ req.getHttpRequest().toAbsolute(url, tmpUrlBuffer);
+ if (isEncodeable(tmpUrlBuffer.toString())) {
+ return (appendSessionId(url, req.getSession().getId()));
+ } else {
+ return (url);
+ }
+
+ }
+
+
+ /**
+ * Encode the session identifier associated with this response
+ * into the specified redirect URL, if necessary.
+ *
+ * @param url URL to be encoded
+ *
+ * @deprecated As of Version 2.1 of the Java Servlet API, use
+ * <code>encodeRedirectURL()</code> instead.
+ */
+ public String encodeRedirectUrl(String url) {
+ return (encodeRedirectURL(url));
+ }
+
+
+ /**
+ * Encode the session identifier associated with this response
+ * into the specified URL, if necessary.
+ *
+ * @param url URL to be encoded
+ */
+ public String encodeURL(String url) {
+ req.getHttpRequest().toAbsolute(url, tmpUrlBuffer);
+ String absolute = tmpUrlBuffer.toString();
+ if (isEncodeable(absolute)) {
+ // W3c spec clearly said
+ if (url.equalsIgnoreCase("")){
+ url = absolute;
+ }
+ return (appendSessionId(url, req.getSession().getId()));
+ } else {
+ return (url);
+ }
+
+ }
+
+
+ /**
+ * Encode the session identifier associated with this response
+ * into the specified URL, if necessary.
+ *
+ * @param url URL to be encoded
+ *
+ * @deprecated As of Version 2.1 of the Java Servlet API, use
+ * <code>encodeURL()</code> instead.
+ */
+ public String encodeUrl(String url) {
+ return (encodeURL(url));
+ }
+
+
+ /**
+ * Send an error response with the specified status and a
+ * default message.
+ *
+ * @param status HTTP status code to send
+ *
+ * @exception IllegalStateException if this response has
+ * already been committed
+ * @exception IOException if an input/output error occurs
+ */
+ public void sendError(int status)
+ throws IOException {
+ sendError(status, null);
+ }
+
+
+ /**
+ * Send an error response with the specified status and message.
+ *
+ * @param status HTTP status code to send
+ * @param message Corresponding message to send
+ *
+ * @exception IllegalStateException if this response has
+ * already been committed
+ * @exception IOException if an input/output error occurs
+ */
+ public void sendError(int status, String message)
+ throws IOException {
+
+ if (isCommitted())
+ throw new IllegalStateException
+ ("isCommitted");
+
+ // Ignore any call from an included servlet
+ if (included)
+ return;
+
+ setError();
+
+ getHttpResponse().setStatus(status);
+ getHttpResponse().setMessage(message);
+
+ // Clear any data content that has been buffered
+ resetBuffer();
+
+ // Cause the response to be finished (from the application perspective)
+ String statusPage = req.getContext().findStatusPage(status);
+
+ if (statusPage != null) {
+ req.getContext().handleStatusPage(req, this, status, statusPage);
+ } else {
+ // Send a default message body.
+ // TODO: maybe other mechanism to customize default.
+ defaultStatusPage(status, message);
+ }
+ setSuspended(true);
+ }
+
+ /**
+ * Default handler for status code != 200
+ */
+ void defaultStatusPage(int status, String message)
+ throws IOException {
+ setContentType("text/html");
+ if (status > 400 && status < 600) {
+ if (!getHttpResponse().isCommitted()) {
+ resetBuffer();
+ getOutputBuffer().write("<html><body><h1>Status: " +
+ status + "</h1><h1>Message: " + message +
+ "</h1></body></html>");
+ getOutputBuffer().flush();
+ }
+ }
+ }
+
+
+
+ /**
+ * Send a temporary redirect to the specified redirect location URL.
+ *
+ * @param location Location URL to redirect to
+ *
+ * @exception IllegalStateException if this response has
+ * already been committed
+ * @exception IOException if an input/output error occurs
+ */
+ public void sendRedirect(String location)
+ throws IOException {
+
+ if (isCommitted())
+ throw new IllegalStateException
+ ("isCommitted");
+
+ // Ignore any call from an included servlet
+ if (included)
+ return;
+
+ // Clear any data content that has been buffered
+ resetBuffer();
+
+ // Generate a temporary redirect to the specified location
+ try {
+ req.getHttpRequest().toAbsolute(location, tmpUrlBuffer);
+ setStatus(SC_FOUND);
+ resB.getMimeHeaders().setValue("Location").set(tmpUrlBuffer);
+ } catch (IllegalArgumentException e) {
+ setStatus(SC_NOT_FOUND);
+ }
+
+ // Cause the response to be finished (from the application perspective)
+ setSuspended(true);
+
+ }
+
+
+ /**
+ * Set the specified date header to the specified value.
+ *
+ * @param name Name of the header to set
+ * @param value Date value to be set
+ */
+ public void setDateHeader(String name, long value) {
+
+ if (isCommitted())
+ return;
+
+ // Ignore any call from an included servlet
+ if (included) {
+ return;
+ }
+
+ if (format == null) {
+ format = new SimpleDateFormat(HTTP_RESPONSE_DATE_HEADER,
+ Locale.US);
+ format.setTimeZone(TimeZone.getTimeZone("GMT"));
+ }
+
+ setHeader(name, FastHttpDateFormat.formatDate(value, format));
+
+ }
+
+
+ /**
+ * Set the specified header to the specified value.
+ *
+ * @param name Name of the header to set
+ * @param value Value to be set
+ */
+ public void setHeader(String name, String value) {
+
+ if (isCommitted())
+ return;
+
+ // Ignore any call from an included servlet
+ if (included)
+ return;
+
+ getHttpResponse().setHeader(name, value);
+
+ }
+
+
+ /**
+ * Set the specified integer header to the specified value.
+ *
+ * @param name Name of the header to set
+ * @param value Integer value to be set
+ */
+ public void setIntHeader(String name, int value) {
+
+ if (isCommitted())
+ return;
+
+ // Ignore any call from an included servlet
+ if (included)
+ return;
+
+ setHeader(name, "" + value);
+
+ }
+
+
+ /**
+ * Set the HTTP status to be returned with this response.
+ *
+ * @param status The new HTTP status
+ */
+ public void setStatus(int status) {
+ setStatus(status, null);
+ }
+
+
+ /**
+ * Set the HTTP status and message to be returned with this response.
+ *
+ * @param status The new HTTP status
+ * @param message The associated text message
+ *
+ * @deprecated As of Version 2.1 of the Java Servlet API, this method
+ * has been deprecated due to the ambiguous meaning of the message
+ * parameter.
+ */
+ public void setStatus(int status, String message) {
+
+ if (isCommitted())
+ return;
+
+ // Ignore any call from an included servlet
+ if (included)
+ return;
+
+ getHttpResponse().setStatus(status);
+ getHttpResponse().setMessage(message);
+
+ }
+
+
+ // ------------------------------------------------------ Protected Methods
+
+
+ /**
+ * Return <code>true</code> if the specified URL should be encoded with
+ * a session identifier. This will be true if all of the following
+ * conditions are met:
+ * <ul>
+ * <li>The request we are responding to asked for a valid session
+ * <li>The requested session ID was not received via a cookie
+ * <li>The specified URL points back to somewhere within the web
+ * application that is responding to this request
+ * </ul>
+ *
+ * @param location Absolute URL to be validated
+ */
+ protected boolean isEncodeable(final String location) {
+
+ if (location == null)
+ return (false);
+
+ // Is this an intra-document reference?
+ if (location.startsWith("#"))
+ return (false);
+
+ // Are we in a valid session that is not using cookies?
+ final ServletRequestImpl hreq = req;
+ final HttpSession session = hreq.getSession(false);
+ if (session == null)
+ return (false);
+ if (hreq.isRequestedSessionIdFromCookie())
+ return (false);
+
+ // Is this a valid absolute URL?
+ URL url = null;
+ try {
+ url = new URL(location);
+ } catch (MalformedURLException e) {
+ return (false);
+ }
+
+ // Does this URL match down to (and including) the context path?
+ if (!hreq.getScheme().equalsIgnoreCase(url.getProtocol()))
+ return (false);
+ if (!hreq.getServerName().equalsIgnoreCase(url.getHost()))
+ return (false);
+ int serverPort = hreq.getServerPort();
+ if (serverPort == -1) {
+ if ("https".equals(hreq.getScheme()))
+ serverPort = 443;
+ else
+ serverPort = 80;
+ }
+ int urlPort = url.getPort();
+ if (urlPort == -1) {
+ if ("https".equals(url.getProtocol()))
+ urlPort = 443;
+ else
+ urlPort = 80;
+ }
+ if (serverPort != urlPort)
+ return (false);
+
+ String contextPath = req.getContext().getContextPath();
+ if (contextPath != null) {
+ String file = url.getFile();
+ if ((file == null) || !file.startsWith(contextPath))
+ return (false);
+ if( file.indexOf(";jsessionid=" + session.getId()) >= 0 )
+ return (false);
+ }
+
+ // This URL belongs to our web application, so it is encodeable
+ return (true);
+
+ }
+
+
+
+ /**
+ * Return the specified URL with the specified session identifier
+ * suitably encoded.
+ *
+ * @param url URL to be encoded with the session id
+ * @param sessionId Session id to be included in the encoded URL
+ */
+ protected String appendSessionId(String url, String sessionId) {
+
+ if ((url == null) || (sessionId == null))
+ return (url);
+
+ String path = url;
+ String query = "";
+ String anchor = "";
+ int question = url.indexOf('?');
+ if (question >= 0) {
+ path = url.substring(0, question);
+ query = url.substring(question);
+ }
+ int pound = path.indexOf('#');
+ if (pound >= 0) {
+ anchor = path.substring(pound);
+ path = path.substring(0, pound);
+ }
+ StringBuffer sb = new StringBuffer(path);
+ if( sb.length() > 0 ) { // jsessionid can't be first.
+ sb.append(";jsessionid=");
+ sb.append(sessionId);
+ }
+ sb.append(anchor);
+ sb.append(query);
+ return (sb.toString());
+
+ }
+
+ public HttpResponse getHttpResponse() {
+ return resB;
+ }
+
+
+ void setHttpResponse(HttpResponse resB) {
+ this.resB = resB;
+ outputBuffer = resB.getBodyWriter();
+ outputStream = new ServletOutputStreamImpl(resB.getBodyOutputStream());
+ }
+
+
+
+}
+
--- /dev/null
+/*
+ * 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.servlet;
+
+
+import java.io.IOException;
+import java.util.Locale;
+
+import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+
+/**
+ * Wrapper around the response object received as parameter to
+ * RequestDispatcher.include().
+ *
+ * @author Costin Manolache
+ */
+public class ServletResponseIncludeWrapper extends HttpServletResponseWrapper {
+ public ServletResponseIncludeWrapper(ServletResponse current) {
+ super((HttpServletResponse) current);
+ }
+
+ // Not overriden:
+ /*
+ public boolean containsHeader(String name)
+ public String encodeRedirectUrl(String url)
+ public String encodeRedirectURL(String url)
+ public String encodeUrl(String url)
+ public String encodeURL(String url)
+ public void flushBuffer() throws IOException
+ public int getBufferSize()
+ public String getCharacterEncoding()
+ public String getContentType()
+ public Locale getLocale()
+ public ServletOutputStream getOutputStream() throws IOException
+ public ServletResponse getResponse()
+ public PrintWriter getWriter() throws IOException
+ public boolean isCommitted()
+ public void resetBuffer()
+ public void setCharacterEncoding(String charset)
+ public void setResponse(ServletResponse response)
+ */
+
+ public void reset() {
+ if (getResponse().isCommitted())
+ getResponse().reset();
+ else
+ throw new IllegalStateException();
+ }
+
+ public void setContentLength(int len) {
+ }
+
+ public void setContentType(String type) {
+ }
+
+ public void setLocale(Locale loc) {
+ }
+
+ public void setBufferSize(int size) {
+ }
+
+ public void addCookie(Cookie cookie) {
+ }
+
+ public void addDateHeader(String name, long value) {
+ }
+
+ public void addHeader(String name, String value) {
+ }
+
+ public void addIntHeader(String name, int value) {
+ }
+
+ public void sendError(int sc) throws IOException {
+ }
+
+ public void sendError(int sc, String msg) throws IOException {
+ }
+
+ public void sendRedirect(String location) throws IOException {
+ }
+
+ public void setDateHeader(String name, long value) {
+ }
+
+ public void setHeader(String name, String value) {
+ }
+
+ public void setIntHeader(String name, int value) {
+ }
+
+ public void setStatus(int sc) {
+ }
+
+ public void setStatus(int sc, String msg) {
+ }
+}
--- /dev/null
+/*
+ * 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.servlet;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Writer;
+
+/**
+ * Coyote implementation of the servlet writer.
+ *
+ * @author Remy Maucherat
+ */
+public class ServletWriterImpl
+ extends PrintWriter {
+
+
+ // -------------------------------------------------------------- Constants
+
+
+ private static final char[] LINE_SEP = { '\r', '\n' };
+
+
+ // ----------------------------------------------------- Instance Variables
+
+
+ protected Writer ob;
+ protected boolean error = false;
+
+
+ // ----------------------------------------------------------- Constructors
+
+
+ public ServletWriterImpl(Writer ob) {
+ super(ob);
+ this.ob = ob;
+ }
+
+
+ // --------------------------------------------------------- Public Methods
+
+
+ /**
+ * Prevent cloning the facade.
+ */
+ protected Object clone()
+ throws CloneNotSupportedException {
+ throw new CloneNotSupportedException();
+ }
+
+
+ // -------------------------------------------------------- Package Methods
+
+
+ /**
+ * Clear facade.
+ */
+ void clear() {
+ ob = null;
+ }
+
+
+ /**
+ * Recycle.
+ */
+ void recycle() {
+ error = false;
+ }
+
+
+ // --------------------------------------------------------- Writer Methods
+
+
+ public void flush() {
+
+ if (error)
+ return;
+
+ try {
+ ob.flush();
+ } catch (IOException e) {
+ error = true;
+ }
+
+ }
+
+
+ public void close() {
+
+ // We don't close the PrintWriter - super() is not called,
+ // so the stream can be reused. We close ob.
+ try {
+ ob.close();
+ } catch (IOException ex ) {
+ ;
+ }
+ error = false;
+
+ }
+
+
+ public boolean checkError() {
+ flush();
+ return error;
+ }
+
+
+ public void write(int c) {
+
+ if (error)
+ return;
+
+ try {
+ ob.write(c);
+ } catch (IOException e) {
+ error = true;
+ }
+
+ }
+
+
+ public void write(char buf[], int off, int len) {
+
+ if (error)
+ return;
+
+ try {
+ ob.write(buf, off, len);
+ } catch (IOException e) {
+ error = true;
+ }
+
+ }
+
+
+ public void write(char buf[]) {
+ write(buf, 0, buf.length);
+ }
+
+
+ public void write(String s, int off, int len) {
+
+ if (error)
+ return;
+
+ try {
+ ob.write(s, off, len);
+ } catch (IOException e) {
+ error = true;
+ }
+
+ }
+
+
+ public void write(String s) {
+ write(s, 0, s.length());
+ }
+
+
+ // ---------------------------------------------------- PrintWriter Methods
+
+
+ public void print(boolean b) {
+ if (b) {
+ write("true");
+ } else {
+ write("false");
+ }
+ }
+
+
+ public void print(char c) {
+ write(c);
+ }
+
+
+ public void print(int i) {
+ write(String.valueOf(i));
+ }
+
+
+ public void print(long l) {
+ write(String.valueOf(l));
+ }
+
+
+ public void print(float f) {
+ write(String.valueOf(f));
+ }
+
+
+ public void print(double d) {
+ write(String.valueOf(d));
+ }
+
+
+ public void print(char s[]) {
+ write(s);
+ }
+
+
+ public void print(String s) {
+ if (s == null) {
+ s = "null";
+ }
+ write(s);
+ }
+
+
+ public void print(Object obj) {
+ write(String.valueOf(obj));
+ }
+
+
+ public void println() {
+ write(LINE_SEP);
+ }
+
+
+ public void println(boolean b) {
+ print(b);
+ println();
+ }
+
+
+ public void println(char c) {
+ print(c);
+ println();
+ }
+
+
+ public void println(int i) {
+ print(i);
+ println();
+ }
+
+
+ public void println(long l) {
+ print(l);
+ println();
+ }
+
+
+ public void println(float f) {
+ print(f);
+ println();
+ }
+
+
+ public void println(double d) {
+ print(d);
+ println();
+ }
+
+
+ public void println(char c[]) {
+ print(c);
+ println();
+ }
+
+
+ public void println(String s) {
+ print(s);
+ println();
+ }
+
+
+ public void println(Object o) {
+ print(o);
+ println();
+ }
+
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.servlet;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+import org.apache.tomcat.integration.ObjectManager;
+import org.apache.tomcat.integration.simple.SimpleObjectManager;
+import org.apache.tomcat.lite.http.DefaultHttpConnector;
+import org.apache.tomcat.lite.http.Dispatcher;
+import org.apache.tomcat.lite.http.HttpChannel;
+import org.apache.tomcat.lite.http.HttpConnector;
+import org.apache.tomcat.lite.http.HttpRequest;
+import org.apache.tomcat.lite.http.HttpResponse;
+import org.apache.tomcat.lite.http.MappingData;
+import org.apache.tomcat.lite.http.HttpChannel.HttpService;
+import org.apache.tomcat.lite.io.WrappedException;
+import org.apache.tomcat.lite.io.CBuffer;
+import org.apache.tomcat.lite.io.MemoryIOConnector;
+import org.apache.tomcat.lite.io.CBuffer;
+
+/**
+ * Helper allowing to run servlets using Tomcat lite http server.
+ *
+ * This is not a full servlet engine - just a small subset allowing
+ * easier transition or reuse.
+ *
+ * @author Costin Manolache
+ */
+public class TomcatLite implements Runnable {
+
+ static ServletApi api = ServletApi.get();
+
+ private String serverDirName;
+ private File workDir;
+
+ // all contexts - hostMapper knows about hostnames and how they are mapped.
+ // this shouldn't be needed if we want to delegate ctx management
+ private ArrayList<ServletContextImpl> contexts = new ArrayList();
+
+ URLClassLoader contextParentLoader;
+ Logger log = Logger.getLogger("TomcatLite");
+ //BaseMapper hostMapper = new BaseMapper();
+
+ // Servlets to preload in each context, configurable from CLI or API
+ Map<String,String> preloadServlets = new HashMap();
+ Map<String,String> preloadMappings = new HashMap();
+
+ Map<String,String> ctxDefaultInitParam = new HashMap();
+
+ ObjectManager om;
+
+ private HttpConnector httpConnector;
+
+ static String SERVLETS_PACKAGE = "org.apache.tomcat.servlets";
+
+ // can be set to ConfigLoader to skip auto-parsing web.xml
+ private String deployListener =
+ "org.apache.tomcat.servlets.config.deploy.WarDeploy";
+
+ int port = 8080;
+
+ public TomcatLite() {
+ }
+
+ public TomcatLite(ObjectManager om) {
+ this.setObjectManager(om);
+ }
+
+ // --------------- start/stop ---------------
+
+ public static ObjectManager defaultObjectManager() {
+ SimpleObjectManager cfg = new SimpleObjectManager();
+ return cfg;
+ }
+ /**
+ * Return the object manager associated with this tomcat.
+ * If none set, create a minimal one with the default
+ * values.
+ */
+ public ObjectManager getObjectManager() {
+ if (om == null) {
+ om = defaultObjectManager();
+ om.bind("TomcatLite", this);
+ }
+ return om;
+ }
+
+ public void setObjectManager(ObjectManager om) {
+ this.om = om;
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ public List/*<ServletContextImpl>*/ getWebapps() {
+ return contexts;
+ }
+
+ public URLClassLoader getContextParentLoader() {
+ if (contextParentLoader == null) {
+
+ ClassLoader parent = this.getClass().getClassLoader();
+ contextParentLoader = new URLClassLoader(new URL[] {},
+ parent);
+
+ /*if (engineRepo == null) {
+ engineRepo = new Repository();
+ engineRepo.setParentClassLoader(parent);
+ }
+
+ contextParentLoader =
+ engineRepo.getClassLoader();
+ */
+ }
+ return contextParentLoader;
+ }
+
+ public void start() throws IOException {
+ long t0 = System.currentTimeMillis();
+
+ // start all contexts
+ // init all contexts
+ Iterator i1 = contexts.iterator();
+ while (i1.hasNext()) {
+ ServletContextImpl ctx = (ServletContextImpl) i1.next();
+ try {
+ ctx.start();
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+ long t1 = System.currentTimeMillis();
+ log.fine("Engine.start() " + (t1-t0));
+ }
+
+
+ /**
+ * Add a context - used for IntrospectionUtils.
+ *
+ * ContextPath:ContextBaseDir
+ */
+ public void setContext(String c) throws ServletException {
+ String[] pathDir = c.split(":", 2);
+ String base = pathDir[0].trim();
+ if (base.length() == 0) {
+ addServletContext("", pathDir[1], null);
+ } else {
+ addServletContext("", pathDir[1], base);
+ }
+ }
+
+ public void setServletContexts(List<ServletContext> c) throws ServletException {
+ for (ServletContext ctx: c) {
+ addServletContext((ServletContextImpl) ctx);
+ }
+ }
+
+ public void setDeployListener(String deploy) {
+ this.deployListener = deploy;
+ }
+
+ public String getDeployListener() {
+ return deployListener;
+ }
+
+ public void setPreload(String servletNameClass) {
+ String[] nv = servletNameClass.split(":");
+ preloadServlets.put(nv[0], nv[1]);
+ }
+
+ public void addPreload(String servletName, String servletClassName) {
+ preloadServlets.put(servletName, servletClassName);
+ }
+
+ public void setDefaultInitParam(String nameValue) {
+ String[] nv = nameValue.split(":");
+ ctxDefaultInitParam.put(nv[0], nv[1]);
+ }
+
+ public void addDefaultInitParam(String name, String value) {
+ ctxDefaultInitParam.put(name, value);
+ }
+
+ public void setPreloadMappings(String servletPath) {
+ String[] nv = servletPath.split(":");
+ preloadMappings.put(nv[0], nv[1]);
+ }
+
+ public void addPreloadMapping(String servletName, String path) {
+ preloadMappings.put(servletName, path);
+ }
+
+ public void stop() {
+ Iterator i1 = contexts.iterator();
+ while (i1.hasNext()) {
+ ServletContextImpl ctx = (ServletContextImpl) i1.next();
+ try {
+ ctx.destroy();
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+ try {
+ stopConnector();
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ // -------------- Context add/remove --------------
+
+ public static String[] DEFAULT_WELCOME = { "index.html" };
+
+ public void addServletContext(ServletContextImpl ctx) throws ServletException {
+ ctx.setTomcat(this);
+
+ getDispatcher().addContext(ctx.getHostname(),
+ ctx.getContextPath(), ctx, null, null, ctxService);
+
+ contexts.add(ctx);
+
+ getObjectManager().bind("ServletContext:" +
+ ctx.getHostname() + ctx.getContextPath(),
+ ctx);
+
+ }
+
+ /**
+ * Add a context.
+ *
+ * web.xml will be read as part of init, and the initialization will be
+ * part of start or lazy.
+ *
+ * @param hostname - ""
+ * if default host, or string to be matched with Host header
+ * @param basePath = directory where the webapp is installed
+ * @param path -
+ * context path, "/" for root, "/examples", etc
+ * @return a servlet context
+ * @throws ServletException
+ */
+ public ServletContextImpl addServletContext(String hostname,
+ String basePath,
+ String path)
+ throws ServletException
+ {
+ ServletContextImpl ctx = api.newContext();
+ ctx.setContextPath(path);
+ ctx.setBasePath(basePath);
+ addServletContext(ctx);
+ return ctx;
+ }
+
+ public static ServletRequestImpl getFacade(HttpRequest req) {
+ ServletRequestImpl sreq = (ServletRequestImpl) req.wrapperRequest;
+ if (sreq == null) {
+ sreq = api.newRequest(req);
+ req.wrapperRequest = sreq;
+ req.nativeRequest = req.getHttpChannel(); // TODO ?
+
+ sreq.getResponse().setHttpResponse(req.getHttpChannel().getResponse());
+ }
+ return sreq;
+ }
+
+ public void removeServletContext(ServletContext sctx)
+ throws ServletException
+ {
+ ServletContextImpl ctx = (ServletContextImpl) sctx;
+ // TODO: destroy all servlets and filters
+ // TODO: clean up any other reference to the context or its loader
+ notifyRemove(ctx);
+ }
+
+ public Dispatcher getDispatcher() {
+ return getHttpConnector().getDispatcher();
+ }
+
+ /**
+ * Required for ServletContext.getContext(uri);
+ * @throws ServletException
+ * @throws IOException
+ */
+ public ServletContextImpl getContext(ServletContextImpl impl, String uri)
+ throws IOException, ServletException {
+ MappingData md = new MappingData();
+ CBuffer hostMB = CBuffer.newInstance();
+ CBuffer urlMB = CBuffer.newInstance();
+ hostMB.set(impl.getHostname());
+ urlMB.set(uri);
+ getDispatcher().map(hostMB, urlMB, md);
+ return (ServletContextImpl) md.context;
+ }
+
+ HttpService ctxService = new HttpService() {
+
+ @Override
+ public void service(HttpRequest httpReq, HttpResponse httpRes)
+ throws IOException {
+ HttpChannel client = httpReq.getHttpChannel();
+
+ ServletRequestImpl req = getFacade(client.getRequest());
+ ServletResponseImpl res = req.getResponse();
+
+ try {
+ TomcatLite.this.service(req, res);
+
+
+ if (!req.isAsyncStarted()) {
+ // Recycle the facade objects -
+ // low level recycled by connector
+
+ // Not an actual flush - only goes to next
+ res.getOutputBuffer().push();
+
+ req.recycle();
+ }
+ } catch (IOException ex) {
+ throw ex;
+ } catch (Throwable t) {
+ throw new WrappedException(t);
+ }
+ }
+ };
+
+ /**
+ * Service a request.
+ * The response is not flushed, and we don't recycle at the end.
+ */
+ public void service(ServletRequestImpl req, ServletResponseImpl res)
+ throws Exception, IOException {
+
+ // TODO: move later
+ req.parseSessionId();
+
+ MappingData mapRes = req.getMappingData();
+ ServletContextImpl ctx = (ServletContextImpl)mapRes.context;
+ try {
+ // context wrapper;
+ mapRes.service = null;
+
+ getDispatcher().map(ctx.getContextMap(), req.getHttpRequest().decodedURI(), mapRes);
+
+ // Possible redirect
+ CBuffer redirectPathMB = mapRes.redirectPath;
+ if (redirectPathMB.length() != 0) {
+ CBuffer redirectPath = CBuffer.newInstance();
+ req.getHttpRequest().getUrlEncoding()
+ .urlEncode(redirectPathMB,
+ redirectPath, req.getHttpRequest().getCharEncoder());
+
+ String query = req.getQueryString();
+ if (req.isRequestedSessionIdFromURL()) {
+ // This is not optimal, but as this is not very common, it
+ // shouldn't matter
+ redirectPath.append(";")
+ .append(ServletRequestImpl.SESSION_PARAMETER_NAME)
+ .append("=")
+ .append(req.getRequestedSessionId());
+ }
+ if (query != null) {
+ // This is not optimal, but as this is not very common, it
+ // shouldn't matter
+ redirectPath.append("?").append(query);
+ }
+ res.sendRedirect(redirectPath.toString());
+ return;
+ }
+
+ req.setContext(ctx);
+ req.parseSessionCookiesId();
+
+ // bind class loader
+ Thread.currentThread().setContextClassLoader(ctx.getClassLoader());
+
+ ServletConfigImpl h = (ServletConfigImpl) mapRes.getServiceObject();
+ if (h != null) {
+ req.setWrapper((ServletConfigImpl)mapRes.getServiceObject());
+ h.serviceServlet(ctx, req, res, h, mapRes );
+ }
+ } catch (Throwable t) {
+ log.log(Level.INFO, ctx.contextPath + ": " + req.getRequest() +
+ ": User exception in servlet ", t);
+ } finally {
+ if(mapRes != null )
+ mapRes.recycle();
+ }
+ }
+
+
+ // ------------ Notifications for JMX ----------------
+
+ void notifyAdd(Object o) {
+ }
+
+ void notifyRemove(Object o) {
+ }
+
+ public void setServerDir(String dir) {
+ this.serverDirName = dir;
+ }
+
+ public File getWork() {
+ if (workDir == null) {
+ if (serverDirName == null) {
+ serverDirName = "./";
+ }
+ File rootDirFile = new File(serverDirName);
+ workDir = new File(rootDirFile, "tomcat-work");
+ if (workDir.exists()) {
+ workDir.mkdirs();
+ }
+ }
+ return workDir;
+ }
+
+ /**
+ * Load all context configs, loads the connector
+ *
+ * @throws ServletException
+ * @throws IOException
+ */
+ public void init() throws ServletException, IOException {
+ if (contexts.size() == 0) {
+ setContext("/:./webapps/ROOT");
+ }
+ Iterator i1 = contexts.iterator();
+ while (i1.hasNext()) {
+ ServletContextImpl ctx = (ServletContextImpl) i1.next();
+ try {
+ ctx.loadConfig();
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public void startConnector() throws IOException {
+ getHttpConnector().setPort(port);
+ getHttpConnector().start();
+ }
+
+ public void stopConnector() throws Exception {
+ getHttpConnector().stop();
+ }
+
+ public void run() {
+ try {
+ execute();
+ } catch (ServletException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void execute() throws ServletException, IOException {
+ init();
+ start();
+ startConnector();
+ }
+
+ void setHttpConnector(HttpConnector httpConnector) {
+ this.httpConnector = httpConnector;
+ }
+
+ public HttpConnector getHttpConnector() {
+ if (httpConnector == null) {
+ httpConnector = DefaultHttpConnector.get();
+ }
+ return httpConnector;
+ }
+
+ HttpConnector local;
+
+ public HttpConnector getLocalConnector() {
+ if (local == null) {
+ local = new HttpConnector(new MemoryIOConnector());
+ }
+ return local;
+ }
+}
--- /dev/null
+/*
+ * 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.servlet;
+
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.ArrayList;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.tomcat.servlets.util.RequestUtil;
+
+/**
+ * First filter after the context and servlet are mapped. It will add
+ * web.xml-defined filters.
+ *
+ * costin: This is another mapping - done in RequestDispatcher or initial
+ * mapping.
+ * Also: StandardHostValve - sets attribute for error pages,
+ * StandardWrapperValve - mapping per invocation
+ *
+ * @author Greg Murray
+ * @author Remy Maucherat
+ */
+public class WebappFilterMapper implements Filter {
+
+
+ // -------------------------------------------------------------- Constants
+
+
+ public static final int ERROR = 1;
+ public static final Integer ERROR_INTEGER = new Integer(ERROR);
+ public static final int FORWARD = 2;
+ public static final Integer FORWARD_INTEGER = new Integer(FORWARD);
+ public static final int INCLUDE = 4;
+ public static final Integer INCLUDE_INTEGER = new Integer(INCLUDE);
+ public static final int REQUEST = 8;
+ public static final Integer REQUEST_INTEGER = new Integer(REQUEST);
+
+ /**
+ * Request dispatcher state.
+ */
+ public static final String DISPATCHER_TYPE_ATTR =
+ "org.apache.catalina.core.DISPATCHER_TYPE";
+
+ /**
+ * Request dispatcher path.
+ */
+ public static final String DISPATCHER_REQUEST_PATH_ATTR =
+ "org.apache.catalina.core.DISPATCHER_REQUEST_PATH";
+
+
+ // ----------------------------------------------------------- Constructors
+ ServletContextImpl servletContext;
+
+ public WebappFilterMapper() {
+ }
+
+ public WebappFilterMapper(ServletContextImpl impl) {
+ servletContext = impl;
+ }
+
+ public void setServletContext(ServletContextImpl sc) {
+ servletContext = sc;
+ }
+
+ // --------------------------------------------------------- Public Methods
+
+ ArrayList<FilterMap> filterMaps = new ArrayList();
+
+ public void addMapping(String filterName,
+ String url,
+ String servletName,
+ String type[], boolean isMatchAfter) {
+ FilterMap map = new FilterMap();
+ map.setURLPattern(url);
+ map.setFilterName(filterName);
+ map.setServletName(servletName);
+ if (isMatchAfter) {
+ filterMaps.add(map);
+ } else {
+ filterMaps.add(0, map);
+ }
+ }
+
+ /**
+ * Construct and return a FilterChain implementation that will wrap the
+ * execution of the specified servlet instance. If we should not execute
+ * a filter chain at all, return <code>null</code>.
+ *
+ * @param request The servlet request we are processing
+ * @param servlet The servlet instance to be wrapped
+ */
+ public FilterChainImpl createFilterChain(ServletRequest request,
+ ServletConfigImpl wrapper,
+ Servlet servlet) {
+
+ // If there is no servlet to execute, return null
+ if (servlet == null)
+ return (null);
+
+ // get the dispatcher type
+ int dispatcher = -1;
+ if (request.getAttribute(DISPATCHER_TYPE_ATTR) != null) {
+ Integer dispatcherInt =
+ (Integer) request.getAttribute(DISPATCHER_TYPE_ATTR);
+ dispatcher = dispatcherInt.intValue();
+ }
+ String requestPath = null;
+ Object attribute = request.getAttribute(DISPATCHER_REQUEST_PATH_ATTR);
+
+ if (attribute != null){
+ requestPath = attribute.toString();
+ }
+
+ HttpServletRequest hreq = null;
+ if (request instanceof HttpServletRequest)
+ hreq = (HttpServletRequest)request;
+
+ // Create and initialize a filter chain object
+ FilterChainImpl filterChain = null;
+ if ((request instanceof ServletRequestImpl)) {
+ ServletRequestImpl req = (ServletRequestImpl) request;
+ filterChain = (FilterChainImpl) req.getFilterChain();
+ filterChain.release();
+ } else {
+ // Security: Do not recycle
+ filterChain = new FilterChainImpl();
+ }
+
+ filterChain.setServlet(wrapper, servlet);
+
+ // If there are no filter mappings, we are done
+ if ((filterMaps.size() == 0))
+ return (filterChain);
+
+ // Acquire the information we will need to match filter mappings
+ String servletName = wrapper.getServletName();
+
+ int n = 0;
+
+ // TODO(costin): optimize: separate in 2 lists, one for url-mapped, one for
+ // servlet-name. Maybe even separate list for dispatcher and
+ // non-dispatcher
+
+ // TODO(costin): optimize: set the FilterConfig in the FilterMap, to
+ // avoid second hash lookup
+
+ // Add the relevant path-mapped filters to this filter chain
+ for (int i = 0; i < filterMaps.size(); i++) {
+ FilterMap filterMap = (FilterMap)filterMaps.get(i);
+ if (!matchDispatcher(filterMap ,dispatcher)) {
+ continue;
+ }
+ if (!matchFiltersURL(filterMap, requestPath))
+ continue;
+ FilterConfigImpl filterConfig =
+ servletContext.getFilter(filterMap.getFilterName());
+ if (filterConfig == null) {
+ // FIXME - log configuration problem
+ continue;
+ }
+ filterChain.addFilter(filterConfig);
+ n++;
+ }
+
+ // Add filters that match on servlet name second
+ for (int i = 0; i < filterMaps.size(); i++) {
+ FilterMap filterMap = (FilterMap)filterMaps.get(i);
+ if (!matchDispatcher(filterMap ,dispatcher)) {
+ continue;
+ }
+ if (!matchFiltersServlet(filterMap, servletName))
+ continue;
+ FilterConfigImpl filterConfig =
+ servletContext.getFilter(filterMap.getFilterName());
+ if (filterConfig == null) {
+ ; // FIXME - log configuration problem
+ continue;
+ }
+ filterChain.addFilter(filterConfig);
+ n++;
+ }
+
+ // Return the completed filter chain
+ return (filterChain);
+
+ }
+
+
+ // -------------------------------------------------------- Private Methods
+
+
+ /**
+ * Return <code>true</code> if the context-relative request path
+ * matches the requirements of the specified filter mapping;
+ * otherwise, return <code>null</code>.
+ *
+ * @param filterMap Filter mapping being checked
+ * @param requestPath Context-relative request path of this request
+ */
+ private boolean matchFiltersURL(FilterMap filterMap, String requestPath) {
+
+ if (requestPath == null)
+ return (false);
+
+ // Match on context relative request path
+ String testPath = filterMap.getURLPattern();
+ if (testPath == null)
+ return (false);
+
+ // Case 1 - Exact Match
+ if (testPath.equals(requestPath))
+ return (true);
+
+ // Case 2 - Path Match ("/.../*")
+ if (testPath.equals("/*"))
+ return (true);
+ if (testPath.endsWith("/*")) {
+ if (testPath.regionMatches(0, requestPath, 0,
+ testPath.length() - 2)) {
+ if (requestPath.length() == (testPath.length() - 2)) {
+ return (true);
+ } else if ('/' == requestPath.charAt(testPath.length() - 2)) {
+ return (true);
+ }
+ }
+ return (false);
+ }
+
+ // Case 3 - Extension Match
+ if (testPath.startsWith("*.")) {
+ int slash = requestPath.lastIndexOf('/');
+ int period = requestPath.lastIndexOf('.');
+ if ((slash >= 0) && (period > slash)
+ && (period != requestPath.length() - 1)
+ && ((requestPath.length() - period)
+ == (testPath.length() - 1))) {
+ return (testPath.regionMatches(2, requestPath, period + 1,
+ testPath.length() - 2));
+ }
+ }
+
+ // Case 4 - "Default" Match
+ return (false); // NOTE - Not relevant for selecting filters
+
+ }
+
+
+ /**
+ * Return <code>true</code> if the specified servlet name matches
+ * the requirements of the specified filter mapping; otherwise
+ * return <code>false</code>.
+ *
+ * @param filterMap Filter mapping being checked
+ * @param servletName Servlet name being checked
+ */
+ private boolean matchFiltersServlet(FilterMap filterMap,
+ String servletName) {
+
+ if (servletName == null) {
+ return (false);
+ } else {
+ if (servletName.equals(filterMap.getServletName())) {
+ return (true);
+ } else {
+ return false;
+ }
+ }
+
+ }
+
+
+ /**
+ * Convienience method which returns true if the dispatcher type
+ * matches the dispatcher types specified in the FilterMap
+ */
+ private boolean matchDispatcher(FilterMap filterMap, int dispatcher) {
+ switch (dispatcher) {
+ case FORWARD : {
+ if (filterMap.getDispatcherMapping() == FilterMap.FORWARD ||
+ filterMap.getDispatcherMapping() == FilterMap.FORWARD_ERROR ||
+ filterMap.getDispatcherMapping() == FilterMap.INCLUDE_FORWARD ||
+ filterMap.getDispatcherMapping() == FilterMap.INCLUDE_ERROR_FORWARD ||
+ filterMap.getDispatcherMapping() == FilterMap.REQUEST_FORWARD ||
+ filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD ||
+ filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD_INCLUDE ||
+ filterMap.getDispatcherMapping() == FilterMap.REQUEST_FORWARD_INCLUDE) {
+ return true;
+ }
+ break;
+ }
+ case INCLUDE : {
+ if (filterMap.getDispatcherMapping() == FilterMap.INCLUDE ||
+ filterMap.getDispatcherMapping() == FilterMap.INCLUDE_ERROR ||
+ filterMap.getDispatcherMapping() == FilterMap.INCLUDE_FORWARD ||
+ filterMap.getDispatcherMapping() == FilterMap.INCLUDE_ERROR_FORWARD ||
+ filterMap.getDispatcherMapping() == FilterMap.REQUEST_INCLUDE ||
+ filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_INCLUDE ||
+ filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD_INCLUDE ||
+ filterMap.getDispatcherMapping() == FilterMap.REQUEST_FORWARD_INCLUDE) {
+ return true;
+ }
+ break;
+ }
+ case REQUEST : {
+ if (filterMap.getDispatcherMapping() == FilterMap.REQUEST ||
+ filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR ||
+ filterMap.getDispatcherMapping() == FilterMap.REQUEST_INCLUDE ||
+ filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_INCLUDE ||
+ filterMap.getDispatcherMapping() == FilterMap.REQUEST_FORWARD ||
+ filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD ||
+ filterMap.getDispatcherMapping() == FilterMap.REQUEST_FORWARD_INCLUDE ||
+ filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD_INCLUDE) {
+ return true;
+ }
+ break;
+ }
+ case ERROR : {
+ if (filterMap.getDispatcherMapping() == FilterMap.ERROR ||
+ filterMap.getDispatcherMapping() == FilterMap.FORWARD_ERROR ||
+ filterMap.getDispatcherMapping() == FilterMap.INCLUDE_ERROR ||
+ filterMap.getDispatcherMapping() == FilterMap.INCLUDE_ERROR_FORWARD ||
+ filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR ||
+ filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD ||
+ filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD_INCLUDE ||
+ filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_INCLUDE) {
+ return true;
+ }
+ break;
+ }
+ }
+ return false;
+ }
+
+
+ // -------------------- Map elements -----------------------
+
+ public static class FilterMap implements Serializable {
+
+
+ // ------------------------------------------------------------- Properties
+
+
+ /**
+ * The name of this filter to be executed when this mapping matches
+ * a particular request.
+ */
+
+ public static final int ERROR = 1;
+ public static final int FORWARD = 2;
+ public static final int FORWARD_ERROR =3;
+ public static final int INCLUDE = 4;
+ public static final int INCLUDE_ERROR = 5;
+ public static final int INCLUDE_ERROR_FORWARD =6;
+ public static final int INCLUDE_FORWARD = 7;
+ public static final int REQUEST = 8;
+ public static final int REQUEST_ERROR = 9;
+ public static final int REQUEST_ERROR_FORWARD = 10;
+ public static final int REQUEST_ERROR_FORWARD_INCLUDE = 11;
+ public static final int REQUEST_ERROR_INCLUDE = 12;
+ public static final int REQUEST_FORWARD = 13;
+ public static final int REQUEST_INCLUDE = 14;
+ public static final int REQUEST_FORWARD_INCLUDE= 15;
+
+ // represents nothing having been set. This will be seen
+ // as equal to a REQUEST
+ private static final int NOT_SET = -1;
+
+ private int dispatcherMapping=NOT_SET;
+
+ private String filterName = null;
+
+ /**
+ * The URL pattern this mapping matches.
+ */
+ private String urlPattern = null;
+
+ /**
+ * The servlet name this mapping matches.
+ */
+ private String servletName = null;
+
+
+
+ public String getFilterName() {
+ return (this.filterName);
+ }
+
+ public void setFilterName(String filterName) {
+ this.filterName = filterName;
+ }
+
+
+ public String getServletName() {
+ return (this.servletName);
+ }
+
+ public void setServletName(String servletName) {
+ this.servletName = servletName;
+ }
+
+
+ public String getURLPattern() {
+ return (this.urlPattern);
+ }
+
+ public void setURLPattern(String urlPattern) {
+ this.urlPattern = RequestUtil.URLDecode(urlPattern);
+ }
+
+ /**
+ *
+ * This method will be used to set the current state of the FilterMap
+ * representing the state of when filters should be applied:
+ *
+ * ERROR
+ * FORWARD
+ * FORWARD_ERROR
+ * INCLUDE
+ * INCLUDE_ERROR
+ * INCLUDE_ERROR_FORWARD
+ * REQUEST
+ * REQUEST_ERROR
+ * REQUEST_ERROR_INCLUDE
+ * REQUEST_ERROR_FORWARD_INCLUDE
+ * REQUEST_INCLUDE
+ * REQUEST_FORWARD,
+ * REQUEST_FORWARD_INCLUDE
+ *
+ */
+ public void setDispatcher(String dispatcherString) {
+ String dispatcher = dispatcherString.toUpperCase();
+
+ if (dispatcher.equals("FORWARD")) {
+
+ // apply FORWARD to the global dispatcherMapping.
+ switch (dispatcherMapping) {
+ case NOT_SET : dispatcherMapping = FORWARD; break;
+ case ERROR : dispatcherMapping = FORWARD_ERROR; break;
+ case INCLUDE : dispatcherMapping = INCLUDE_FORWARD; break;
+ case INCLUDE_ERROR : dispatcherMapping = INCLUDE_ERROR_FORWARD; break;
+ case REQUEST : dispatcherMapping = REQUEST_FORWARD; break;
+ case REQUEST_ERROR : dispatcherMapping = REQUEST_ERROR_FORWARD; break;
+ case REQUEST_ERROR_INCLUDE : dispatcherMapping = REQUEST_ERROR_FORWARD_INCLUDE; break;
+ case REQUEST_INCLUDE : dispatcherMapping = REQUEST_FORWARD_INCLUDE; break;
+ }
+ } else if (dispatcher.equals("INCLUDE")) {
+ // apply INCLUDE to the global dispatcherMapping.
+ switch (dispatcherMapping) {
+ case NOT_SET : dispatcherMapping = INCLUDE; break;
+ case ERROR : dispatcherMapping = INCLUDE_ERROR; break;
+ case FORWARD : dispatcherMapping = INCLUDE_FORWARD; break;
+ case FORWARD_ERROR : dispatcherMapping = INCLUDE_ERROR_FORWARD; break;
+ case REQUEST : dispatcherMapping = REQUEST_INCLUDE; break;
+ case REQUEST_ERROR : dispatcherMapping = REQUEST_ERROR_INCLUDE; break;
+ case REQUEST_ERROR_FORWARD : dispatcherMapping = REQUEST_ERROR_FORWARD_INCLUDE; break;
+ case REQUEST_FORWARD : dispatcherMapping = REQUEST_FORWARD_INCLUDE; break;
+ }
+ } else if (dispatcher.equals("REQUEST")) {
+ // apply REQUEST to the global dispatcherMapping.
+ switch (dispatcherMapping) {
+ case NOT_SET : dispatcherMapping = REQUEST; break;
+ case ERROR : dispatcherMapping = REQUEST_ERROR; break;
+ case FORWARD : dispatcherMapping = REQUEST_FORWARD; break;
+ case FORWARD_ERROR : dispatcherMapping = REQUEST_ERROR_FORWARD; break;
+ case INCLUDE : dispatcherMapping = REQUEST_INCLUDE; break;
+ case INCLUDE_ERROR : dispatcherMapping = REQUEST_ERROR_INCLUDE; break;
+ case INCLUDE_FORWARD : dispatcherMapping = REQUEST_FORWARD_INCLUDE; break;
+ case INCLUDE_ERROR_FORWARD : dispatcherMapping = REQUEST_ERROR_FORWARD_INCLUDE; break;
+ }
+ } else if (dispatcher.equals("ERROR")) {
+ // apply ERROR to the global dispatcherMapping.
+ switch (dispatcherMapping) {
+ case NOT_SET : dispatcherMapping = ERROR; break;
+ case FORWARD : dispatcherMapping = FORWARD_ERROR; break;
+ case INCLUDE : dispatcherMapping = INCLUDE_ERROR; break;
+ case INCLUDE_FORWARD : dispatcherMapping = INCLUDE_ERROR_FORWARD; break;
+ case REQUEST : dispatcherMapping = REQUEST_ERROR; break;
+ case REQUEST_INCLUDE : dispatcherMapping = REQUEST_ERROR_INCLUDE; break;
+ case REQUEST_FORWARD : dispatcherMapping = REQUEST_ERROR_FORWARD; break;
+ case REQUEST_FORWARD_INCLUDE : dispatcherMapping = REQUEST_ERROR_FORWARD_INCLUDE; break;
+ }
+ }
+ }
+
+ public int getDispatcherMapping() {
+ // per the SRV.6.2.5 absence of any dispatcher elements is
+ // equivelant to a REQUEST value
+ if (dispatcherMapping == NOT_SET) return REQUEST;
+ else return dispatcherMapping;
+ }
+
+ }
+
+
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+
+ public void doFilter(ServletRequest request, ServletResponse response,
+ FilterChain chain)
+ throws IOException, ServletException {
+ }
+
+
+ public void destroy() {
+ }
+
+}