/* Copyright 2005-2006 Tim Fennell * * 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 net.sourceforge.stripes.mock; import javax.servlet.*; import javax.servlet.descriptor.JspConfigDescriptor; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; /** * <p>Mock implementation of a ServletContext. Provides implementation the most commonly used * methods, namely those to manipulate init parameters and attributes. Additional methods are * provided to allow the setting of initialization parameters etc.</p> * * <p>This mock implementation is meant only for testing purposes. As such there are certain * limitations:</p> * * <ul> * <li>All configured Filters are applied to every request</li> * <li>Only a single servlet is supported, and all requests are routed to it.</li> * <li>Forwards, includes and redirects are recorded for posterity, but not processed.</li> * <li>It may or may not be thread safe (not a priority since it is mainly for unit testing).</li> * <li>You do your own session management (attach one to a request before executing).</li> * </ul> * * @author Tim Fennell * @since Stripes 1.1.1 */ public class MockServletContext implements ServletContext { private String contextName; private Map<String,String> initParameters = new HashMap<String,String>(); private Map<String,Object> attributes = new HashMap<String,Object>(); private List<Filter> filters = new ArrayList<Filter>(); private List<ServletContextListener> listeners = new ArrayList<ServletContextListener>(); private HttpServlet servlet; /** Simple constructor that creates a new mock ServletContext with the supplied context name. */ public MockServletContext(String contextName) { this.contextName = contextName; } /** If the url is within this servlet context, returns this. Otherwise returns null. */ public ServletContext getContext(String url) { if (url.startsWith("/" + this.contextName)) { return this; } else { return null; } } /** Servlet 2.3 method. Returns the context name with a leading slash. */ public String getContextPath() { return "/" + this.contextName; } /** Always returns 2. */ public int getMajorVersion() { return 2; } /** Always returns 4. */ public int getMinorVersion() { return 4; } /** Always returns null (i.e. don't know). */ public String getMimeType(String file) { return null; } /** Always returns null (i.e. there are no resources under this path). */ public Set<String> getResourcePaths(String path) { return null; } /** Uses the current classloader to fetch the resource if it can. */ public URL getResource(String name) throws MalformedURLException { while (name.startsWith("/")) name = name.substring(1); return Thread.currentThread().getContextClassLoader().getResource(name); } /** Uses the current classloader to fetch the resource if it can. */ public InputStream getResourceAsStream(String name) { while (name.startsWith("/")) name = name.substring(1); return Thread.currentThread().getContextClassLoader().getResourceAsStream(name); } /** Returns a MockRequestDispatcher for the url provided. */ public RequestDispatcher getRequestDispatcher(String url) { return new MockRequestDispatcher(url); } /** Returns a MockRequestDispatcher for the named servlet provided. */ public RequestDispatcher getNamedDispatcher(String name) { return new MockRequestDispatcher(name); } /** Deprecated method always returns null. */ public Servlet getServlet(String string) throws ServletException { return null; } /** Deprecated method always returns an empty enumeration. */ public Enumeration<Servlet> getServlets() { return Collections.enumeration( Collections.<Servlet>emptySet() ); } /** Deprecated method always returns an empty enumeration. */ public Enumeration<String> getServletNames() { return Collections.enumeration( Collections.<String>emptySet() ); } /** Logs the message to System.out. */ public void log(String message) { System.out.println("MockServletContext: " + message); } /** Logs the message and exception to System.out. */ public void log(Exception exception, String message) { log(message, exception); } /** Logs the message and exception to System.out. */ public void log(String message, Throwable throwable) { log(message); throwable.printStackTrace(System.out); } /** Always returns null as this is standard behaviour for WAR resources. */ public String getRealPath(String string) { return null; } /** Returns a version string identifying the Mock implementation. */ public String getServerInfo() { return "Stripes Mock Servlet Environment, version 1.0."; } /** Adds an init parameter to the mock servlet context. */ public void addInitParameter(String name, String value) { this.initParameters.put(name, value); } /** Adds all the values in the supplied Map to the set of init parameters. */ public void addAllInitParameters(Map<String,String> parameters) { this.initParameters.putAll(parameters); } /** Gets the value of an init parameter with the specified name, if one exists. */ public String getInitParameter(String name) { return this.initParameters.get(name); } /** Returns an enumeration of all the initialization parameters in the context. */ public Enumeration<String> getInitParameterNames() { return Collections.enumeration( this.initParameters.keySet() ); } /** Gets an attribute that has been set on the context (i.e. application) scope. */ public Object getAttribute(String name) { return this.attributes.get(name); } /** Returns an enumeration of all the names of attributes in the context. */ public Enumeration<String> getAttributeNames() { return Collections.enumeration( this.attributes.keySet() ); } /** Sets the supplied value for the attribute on the context. */ public void setAttribute(String name, Object value) { this.attributes.put(name, value); } /** Removes the named attribute from the context. */ public void removeAttribute(String name) { this.attributes.remove(name); } /** Returns the name of the mock context. */ public String getServletContextName() { return this.contextName; } /** Adds a filter to the end of filter chain that will be used to filter requests.*/ public MockServletContext addFilter(Class<? extends Filter> filterClass, String filterName, Map<String,String> initParams) { try { MockFilterConfig config = new MockFilterConfig(); config.setFilterName(filterName); config.setServletContext(this); if (initParams != null) config.addAllInitParameters(initParams); Filter filter = filterClass.newInstance(); filter.init(config); this.filters.add(filter); return this; } catch (Exception e) { throw new RuntimeException("Exception registering new filter with name " + filterName, e); } } /** Removes and destroys all registered filters. */ public MockServletContext removeFilters() { for (Filter each : filters) { try { each.destroy(); } catch (Exception e) { log("Error while destroying filter " + each, e); } } filters.clear(); return this; } /** Provides access to the set of filters configured for this context. */ public List<Filter> getFilters() { return this.filters; } /** Adds a {@link ServletContextListener} to this context and initializes it. */ public MockServletContext addListener(ServletContextListener listener) { ServletContextEvent event = new ServletContextEvent(this); listener.contextInitialized(event); listeners.add(listener); return this; } /**Removes and destroys all registered {@link ServletContextListener}. */ public MockServletContext removeListeners() { ServletContextEvent e = new ServletContextEvent(this); for (ServletContextListener l : listeners) { l.contextDestroyed(e); } listeners.clear(); return this; } /** Sets the servlet that will receive all requests in this servlet context. */ public MockServletContext setServlet(Class<? extends HttpServlet> servletClass, String servletName, Map<String,String> initParams) { try { MockServletConfig config = new MockServletConfig(); config.setServletName(servletName); config.setServletContext(this); if (initParams != null) config.addAllInitParameters(initParams); this.servlet = servletClass.newInstance(); this.servlet.init(config); return this; } catch (Exception e) { throw new RuntimeException("Exception registering servlet with name " + servletName, e); } } /** * <p>Takes a request and response and runs them through the set of filters using a * MockFilterChain, which if everything goes well, will eventually execute the servlet * that is registered with this context.</p> * * <p>Any exceptions that are raised during the processing of the request are simply * passed through to the caller. I.e. they will be thrown from this method.</p> */ public void acceptRequest(MockHttpServletRequest request, MockHttpServletResponse response) throws Exception { copyCookies(request, response); MockFilterChain chain = new MockFilterChain(); chain.setServlet(this.servlet); chain.addFilters(this.filters); chain.doFilter(request, response); // wait for any async context to finish (block) if (request.isAsyncStarted()) { MockAsyncContext asyncContext = request.getAsyncContext(); asyncContext.waitForCompletion(); } } /** * Copies cookies from the request to the response. * * @param request The request. * @param response The response. */ public void copyCookies(MockHttpServletRequest request, MockHttpServletResponse response) { if (request.getCookies() != null) { for (Cookie cookie : request.getCookies()) { response.addCookie(cookie); } } } /** * Closes all filters and servlets for this context (application shutdown). */ public void close() { removeListeners(); removeFilters(); Enumeration<?> servlets = getServlets(); while (servlets.hasMoreElements()) { Object servlet = servlets.nextElement(); if (servlet instanceof Servlet) { try { ((Servlet)servlet).destroy(); } catch (Exception e) { log("Exception caught destroying servlet " + servlet + " contextName=" + contextName, e); } } } } public int getEffectiveMajorVersion() { return 0; } public int getEffectiveMinorVersion() { return 0; } public boolean setInitParameter(String name, String value) { return false; } public ServletRegistration.Dynamic addServlet(String servletName, String className) { return null; } public ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet) { return null; } public ServletRegistration.Dynamic addServlet(String servletName, Class<? extends Servlet> servletClass) { return null; } public <T extends Servlet> T createServlet(Class<T> clazz) throws ServletException { return null; } public ServletRegistration getServletRegistration(String servletName) { return null; } public Map<String, ? extends ServletRegistration> getServletRegistrations() { return null; } public FilterRegistration.Dynamic addFilter(String filterName, String className) { return null; } public FilterRegistration.Dynamic addFilter(String filterName, Filter filter) { return null; } public FilterRegistration.Dynamic addFilter(String filterName, Class<? extends Filter> filterClass) { return null; } public <T extends Filter> T createFilter(Class<T> clazz) throws ServletException { return null; } public FilterRegistration getFilterRegistration(String filterName) { return null; } public Map<String, ? extends FilterRegistration> getFilterRegistrations() { return null; } public SessionCookieConfig getSessionCookieConfig() { return null; } public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes) { } public Set<SessionTrackingMode> getDefaultSessionTrackingModes() { return null; } public Set<SessionTrackingMode> getEffectiveSessionTrackingModes() { return null; } public void addListener(String className) { } public <T extends EventListener> void addListener(T t) { } public void addListener(Class<? extends EventListener> listenerClass) { } public <T extends EventListener> T createListener(Class<T> clazz) throws ServletException { return null; } public JspConfigDescriptor getJspConfigDescriptor() { return null; } public ClassLoader getClassLoader() { return null; } public void declareRoles(String... roleNames) { } public String getVirtualServerName() { return null; } }