package net.sourceforge.javautil.developer.web.unit.mockserver;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.regex.Pattern;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
import javax.servlet.RequestDispatcher;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletException;
import javax.servlet.http.HttpSessionListener;
import net.sourceforge.javautil.common.ReflectionUtil;
import net.sourceforge.javautil.common.reflection.cache.ClassCache;
import net.sourceforge.javautil.web.server.application.WebApplication;
import net.sourceforge.javautil.web.server.application.WebApplicationDefaultMimeMappings;
import net.sourceforge.javautil.web.server.application.WebApplicationDeploymentContext;
import net.sourceforge.javautil.web.server.application.WebApplicationServletContext;
import net.sourceforge.javautil.web.server.descriptor.IWebXml;
import net.sourceforge.javautil.web.server.descriptor.IWebXmlFilter;
import net.sourceforge.javautil.web.server.descriptor.IWebXmlFilterMapping;
import net.sourceforge.javautil.web.server.descriptor.IWebXmlListener;
import net.sourceforge.javautil.web.server.descriptor.IWebXmlServlet;
import net.sourceforge.javautil.web.server.descriptor.IWebXmlServletMapping;
import net.sourceforge.javautil.web.server.descriptor.WebXmlUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This is the main context for actually deploying/undeploying an application.
*
* @author elponderador
* @author $Author$
* @version $Id$
*/
public abstract class MockServletContext implements ServletContext {
protected Logger log;
protected final MockServerHost host;
protected final WebApplication application;
protected final Map<String, MockServletSetup> servlets = new LinkedHashMap<String, MockServletSetup>();
protected final Map<String, MockFilterSetup> filters = new LinkedHashMap<String, MockFilterSetup>();
protected final List<String> listenerClasses = new CopyOnWriteArrayList<String>();
protected final List<ServletContextListener> contextListeners = new ArrayList<ServletContextListener>();
protected final List<HttpSessionListener> sessionListeners = new ArrayList<HttpSessionListener>();
protected final Map<String, String> mimeMappings = new LinkedHashMap<String, String>();
protected final Map<String, String> extMappings = new LinkedHashMap<String, String>();
protected Map<String, Object> attributes = new LinkedHashMap<String, Object>();
protected WebApplicationServletContext wrapper;
public MockServletContext(MockServerHost host, WebApplication application) {
this.host = host;
this.application = application;
this.wrapper = new WebApplicationServletContext(this, application);
this.log = LoggerFactory.getLogger("mockserver.context." + application.getContextPath().replaceAll("[\\\\/]", "."));
this.mimeMappings.putAll(WebApplicationDefaultMimeMappings.getMappings());
this.extMappings.putAll(WebApplicationDefaultMimeMappings.getDefaultExtensionMapping());
}
public List<HttpSessionListener> getSessionListenerClasses () { return new ArrayList<HttpSessionListener>(this.sessionListeners); }
/**
* @param className The name of the class to instantiate, a no-arg constructor is assumed
* @return The new instance of the class
*/
protected Object createInstance (String className) {
return ClassCache.getFor( ReflectionUtil.getClass(className, Thread.currentThread().getContextClassLoader()) )
.newInstance();
}
/**
* This should be called when the context is being deploeyd.
*
* @throws ServletException
*/
protected void initialize () throws ServletException {
ClassLoader original = null;
if (this.application.getClassLoader() != null) {
original = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(this.application.getClassLoader());
}
this.listenerClasses.clear();
this.contextListeners.clear();
this.servlets.clear();
this.filters.clear();
IWebXml webXml = application.getWebXml();
for (IWebXmlListener listener : webXml.getListeners()) {
this.listenerClasses.add(listener.getListenerClass());
}
for (IWebXmlFilter filter : webXml.getFilters()) {
MockFilterSetup setup = new MockFilterSetup(wrapper, filter);
for (IWebXmlFilterMapping mapping : WebXmlUtil.getMappings(webXml, filter)) {
setup.urlPatterns.addAll(mapping.getPatterns());
}
this.filters.put(filter.getFilterName(), setup);
}
for (IWebXmlServlet servlet : webXml.getServlets()) {
MockServletSetup setup = new MockServletSetup(wrapper, servlet, WebXmlUtil.getFilterNames(webXml, servlet));
for (IWebXmlServletMapping mapping : WebXmlUtil.getMappings(webXml, servlet)) {
setup.urlPatterns.addAll(mapping.getPatterns());
}
this.servlets.put(servlet.getServletName(), setup);
}
try {
for (String listener : this.listenerClasses) {
Object linstance = this.createInstance(listener);
if (linstance instanceof ServletContextListener) this.contextListeners.add( (ServletContextListener) linstance );
if (linstance instanceof HttpSessionListener) this.sessionListeners.add( (HttpSessionListener) linstance );
}
ServletContextEvent evt = new ServletContextEvent(wrapper);
for (int cl=0; cl<contextListeners.size(); cl++) contextListeners.get(cl).contextInitialized(evt);
List<MockServletSetup> sservlets = new ArrayList<MockServletSetup>(this.servlets.values());
for (int s=0; s<sservlets.size(); s++) sservlets.get(s).getServlet().init(sservlets.get(s));
List<MockFilterSetup> sfilters = new ArrayList<MockFilterSetup>(this.filters.values());
for (int f=0; f<sfilters.size(); f++) sfilters.get(f).getFilter().init(sfilters.get(f));
} finally {
if (original != null) Thread.currentThread().setContextClassLoader(original);
}
}
/**
* This should be called when the servlet context is to be destroyed/undeployed.
*
* @throws ServletException
*/
protected void destroyed () throws ServletException {
ClassLoader original = null;
if (this.application.getClassLoader() != null) {
original = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(this.application.getClassLoader());
}
try {
List<MockFilterSetup> sfilters = new ArrayList<MockFilterSetup>(this.filters.values());
for (int f=0; f<sfilters.size(); f++) sfilters.get(f).getFilter().destroy();
List<MockServletSetup> sservlets = new ArrayList<MockServletSetup>(this.servlets.values());
for (int s=0; s<sservlets.size(); s++) sservlets.get(s).getServlet().destroy();
ServletContextEvent evt = new ServletContextEvent(this);
for (int cl=0; cl<contextListeners.size(); cl++) contextListeners.get(cl).contextDestroyed(evt);
} finally {
if (original != null) Thread.currentThread().setContextClassLoader(original);
}
}
/**
* @return The wrapper for this for integration
* @see WebApplicationServletContext
*/
public WebApplicationServletContext getWrapper() {
return wrapper;
}
/**
* @param requestPath The requested path
* @return The first/closest match servlet that is defined to handle the requested path, or null if none could be found
*/
public MockServletSetup findServlet (String requestPath) {
List<MockServletSetup> sservlets = new ArrayList<MockServletSetup>(this.servlets.values());
for (int s=0; s<sservlets.size(); s++) {
MockServletSetup servlet = sservlets.get(s);
for (int i=0; i<servlet.getPatterns().size(); i++) {
if (this.matches(requestPath, servlet.getPatterns().get(i))) return servlet;
}
}
return null;
}
/**
* @param servlet The servlet
* @param requestPath The request path
* @return A list of filters that should be included in requests for the servlet/path combination
*/
public List<Filter> getFilters (MockServletSetup servlet, String requestPath) {
List<Filter> filters = new ArrayList<Filter>();
List<MockFilterSetup> sfilters = new ArrayList<MockFilterSetup>(this.filters.values());
for (int f=0; f<sfilters.size(); f++) {
if ( this.isFilterForRequest(sfilters.get(f), servlet, requestPath) )
filters.add( sfilters.get(f).getFilter() );
}
return filters;
}
/**
* @param filter The filter setup
* @param servlet The servlet
* @param requestPath The request path
* @return True if the filter should be included in the request, otherwise false
*/
public boolean isFilterForRequest (MockFilterSetup filter, MockServletSetup servlet, String requestPath) {
if (servlet != null && servlet.filters.contains(filter.getFilterName())) return true;
for (int i=0; i<filter.getPatterns().size(); i++) {
if ( this.matches(requestPath, filter.getPatterns().get(i)) ) return true;
}
return false;
}
/**
* @param requestPath The requested path
* @param urlPattern The pattern for checking
* @return True if the request path matches the pattern, otherwise false
*/
public boolean matches (String requestPath, String urlPattern) {
if (!urlPattern.contains("*") && !urlPattern.contains("?")) {
return requestPath.startsWith(urlPattern);
} else {
urlPattern = urlPattern.replaceAll("\\.", "\\\\.");
if (urlPattern.startsWith("*")) urlPattern = "." + urlPattern;
else if (urlPattern.startsWith("/*")) urlPattern = "/.*";
else if (urlPattern.contains("*")) urlPattern = urlPattern.replaceAll("\\*", ".*");
return Pattern.compile(urlPattern).matcher(requestPath).matches();
}
}
/**
* @return The parent class loader for this context
*/
public ClassLoader getParentClassLoader() { return this.application.getClassLoader(); }
public Object getAttribute(String name) { return attributes.get(name); }
public Enumeration getAttributeNames() { return Collections.enumeration( attributes.keySet() ); }
public ServletContext getContext(String path) {
WebApplicationDeploymentContext deployment = host.getContext(path);
return deployment == null ? null : deployment.getServletContext();
}
public String getContextPath() { return this.application.getContextPath(); }
public String getInitParameter(String name) { return this.application.getWebXml().getInitParameters().get(name); }
public Enumeration getInitParameterNames() {
return Collections.enumeration(this.application.getWebXml().getInitParameters().keySet());
}
public int getMajorVersion() { return 2; }
public void setMimeTypeMapping (String ext, String mimeType) {
this.mimeMappings.put(ext, mimeType);
}
public String getMimeType(String filename) {
String[] extension = filename.split("\\.");
return mimeMappings.get( extension[extension.length-1].toLowerCase() );
}
/**
* @param mimeType The mime content type
* @return The first extension related to this type, otherwise null
*/
public String getDefaultExtension (String mimeType) {
if (this.extMappings.containsKey(mimeType)) return this.extMappings.get(mimeType);
for (String ext : mimeMappings.keySet()) {
if (mimeType.equals(mimeMappings.get(ext))) return ext;
}
return null;
}
public int getMinorVersion() { return 5; }
public RequestDispatcher getNamedDispatcher(String name) {
return this.servlets.containsKey(name) ? this.createRequestDispatcher(this.servlets.get(name), null) : null;
}
public RequestDispatcher getRequestDispatcher(String path) {
MockServletSetup servlet = this.findServlet(path);
return servlet == null ? null : this.createRequestDispatcher(servlet, path);
}
public abstract RequestDispatcher createRequestDispatcher (MockServletSetup servlet, String path);
public String getRealPath(String path) {
File file = application.getResourceResolver().getFile(path);
return file == null ? null : file.getAbsolutePath();
}
public URL getResource(String path) throws MalformedURLException { return application.getResourceResolver().getResource(path); }
public InputStream getResourceAsStream(String path) {
try {
return this.getResource(path).openStream();
} catch (IOException e) {
return null;
}
}
public Set getResourcePaths(String path) {
return new HashSet(application.getResourceResolver().getResourcePaths(path));
}
public String getServerInfo() { return "Gracelets Tools Mock Server"; }
public Servlet getServlet(String name) throws ServletException {
return this.servlets.containsKey(name) ? this.servlets.get(name).getServlet() : null;
}
public String getServletContextName() { return this.application.getName(); }
public Enumeration getServletNames() { return Collections.enumeration( this.servlets.keySet() ); }
public Enumeration getServlets() {
List<Servlet> servlets = new ArrayList<Servlet>();
for (MockServletSetup servlet : this.servlets.values()) servlets.add(servlet.getServlet());
return Collections.enumeration( servlets );
}
public void log(Exception exception, String msg) { log.warn(msg, exception); }
public void log(String msg, Throwable throwable) { log.error(msg, throwable); }
public void log(String msg) { log.info(msg); }
public void removeAttribute(String name) {
Map<String, Object> newm = new LinkedHashMap<String, Object>(this.attributes);
newm.remove(name);
this.attributes = newm;
}
public void setAttribute(String name, Object value) {
Map<String, Object> newm = new LinkedHashMap<String, Object>(this.attributes);
newm.put(name, value);
this.attributes = newm;
}
}