package net.sourceforge.javautil.developer.web.unit.mockserver;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpSession;
import net.sourceforge.javautil.common.CollectionUtil;
import net.sourceforge.javautil.developer.web.unit.mockbrowser.MockBrowserException;
import net.sourceforge.javautil.developer.web.unit.mockbrowser.MockBrowserRequest;
import net.sourceforge.javautil.developer.web.unit.mockserver.http.MockHttpServletContext;
import net.sourceforge.javautil.developer.web.unit.mockserver.http.MockHttpServletRequest;
import net.sourceforge.javautil.developer.web.unit.mockserver.http.MockHttpServletResponse;
import net.sourceforge.javautil.web.server.WebServerApplicationErrorEvent;
import net.sourceforge.javautil.web.server.WebServerApplicationEvent;
import net.sourceforge.javautil.web.server.WebServerException;
import net.sourceforge.javautil.web.server.WebServerHostValve;
import net.sourceforge.javautil.web.server.application.WebApplication;
import net.sourceforge.javautil.web.server.application.WebApplicationDeploymentContext;
import net.sourceforge.javautil.web.server.application.WebApplicationServletContext;
import net.sourceforge.javautil.web.server.descriptor.impl.WebXml;
import net.sourceforge.javautil.web.server.impl.WebServerHostAbstract;
/**
* This will mock a server host.
*
* @author elponderador
* @author $Author$
* @version $Id$
*/
public class MockServerHost extends WebServerHostAbstract<MockServer> {
protected String basePath;
protected List<String> aliases = new ArrayList<String>();
protected Map<String, WebApplicationDeploymentContext> deployed = new LinkedHashMap<String, WebApplicationDeploymentContext>();
public MockServerHost(MockServer server, boolean defaultHost, String name, String basePath) {
super(server, defaultHost, name);
this.basePath = basePath;
}
/**
* @param request The request to process
* @return The response for the request
*
* @throws ServletException
* @throws IOException
*/
public MockServerResponse process (MockBrowserRequest request) throws ServletException, IOException {
ClassLoader original = Thread.currentThread().getContextClassLoader();
try {
String path = request.getUrl().getPath();
WebApplicationDeploymentContext deployment = this.getContext(path);
if (deployment == null)
throw new MockBrowserException(request, "No context could be found to handle request", null);
MockHttpServletContext ctx = (MockHttpServletContext) deployment.getServletContext();
path = path.length() == ctx.getContextPath().length() ? "/" : path.substring(ctx.getContextPath().length());
if (!path.startsWith("/")) path = "/" + path;
Thread.currentThread().setContextClassLoader(ctx.getParentClassLoader());
MockServletSetup servlet = ctx.findServlet(path);
List<Filter> filters = ctx.getFilters(servlet, path);
WebApplicationServletContext wrapper = new WebApplicationServletContext(ctx, ctx.application);
MockHttpServletRequest httpRequest = new MockHttpServletRequest(wrapper, ctx.getSessionManager(), path);
for (String name : request.getCookies().keySet()) {
httpRequest.addCookie(request.getCookies().get(name));
}
if (request.getUrl().getQuery() != null) {
String[] vars = request.getUrl().getQuery().split("&");
for (String var : vars) {
String[] kv = var.split("=");
httpRequest.addParameter(kv[0], kv.length == 1 ? "" : kv[1]);
}
}
MockHttpServletResponse httpResponse = new MockHttpServletResponse(httpRequest, wrapper);
httpRequest.setResponse(httpResponse);
MockFilterChain chain = new MockFilterChain(filters, servlet);
MockServerValve valve = new MockServerValve(server, deployment, httpRequest, httpResponse);
WebServerHostValve[] valves = CollectionUtil.insert(this.valves.toArray(new WebServerHostValve[this.valves.size()]), 0, valve);
MockServerValveContext valveContext = new MockServerValveContext(deployment, httpRequest, httpResponse, chain, valves);
valveContext.next();
return new MockServerResponse(request, ctx, httpRequest, httpResponse);
} finally {
Thread.currentThread().setContextClassLoader(original);
}
}
public List<HttpSession> getSessions(WebApplicationDeploymentContext ctx) {
MockServletContext msc = (MockServletContext) ctx.getServletContext();
if (msc instanceof MockHttpServletContext) {
MockHttpServletContext mhc = (MockHttpServletContext) msc;
List<HttpSession> sessions = new ArrayList<HttpSession>();
for (String id : mhc.getSessionManager().findSessionIds()) {
sessions.add( mhc.getSessionManager().getSession(id) );
}
return sessions;
}
return null;
}
public void addAlias(String alias) { this.aliases.add(alias); }
public boolean handlesHost(String host) { return this.aliases.contains(host); }
public boolean deploy(WebApplication application) {
if (this.deployed.containsKey(name))
throw new WebServerException(server, "An application is already deployed by this name: " + name);
try {
MockHttpServletContext context = new MockHttpServletContext(this, application);
WebApplicationDeploymentContext deployment = new WebApplicationDeploymentContext(this, application, context);
application.preDeploySetup(deployment, false);
context.initialize();
this.deployed.put(application.getName(), deployment);
server.getHandler().applicationDeployed(new WebServerApplicationEvent(server, this, application));
return true;
} catch (Exception e) {
server.getHandler().applicationError(new WebServerApplicationErrorEvent(this, server, application, e, e.getMessage()));
return false;
}
}
public String getApplicationBasePath() {
return this.basePath;
}
public boolean undeploy(String name) {
if (!this.deployed.containsKey(name))
throw new WebServerException(server, "No such application to undeploy: " + name);
try {
((MockHttpServletContext)deployed.get(name).getServletContext()).destroyed();
deployed.get(name).getApplication().postUndeployCleanup(deployed.get(name), false);
server.getHandler().applicationUndeployed(new WebServerApplicationEvent(server, this, deployed.get(name).getApplication()));
deployed.remove(name);
return true;
} catch (Exception e) {
server.getHandler().applicationUndeployed(new WebServerApplicationErrorEvent(this, server, deployed.get(name).getApplication(), e, e.getMessage()));
return false;
}
}
public void postSetup(WebApplication application, WebXml webXml) {
server.getHandler().applicationPreDeployPostSetup(new WebServerApplicationEvent(server, this, application), webXml);
}
public void preCleanup(WebApplication application) {
server.getHandler().applicationPostUndeployPreCleanup(new WebServerApplicationEvent(server, this, application));
}
/**
* @param path The path to resolve for a context
* @return The context that would handle a request at this path, or null if none could be found
*/
public WebApplicationDeploymentContext getContext (String path) {
int match = -1;
WebApplicationDeploymentContext found = null;
for (WebApplicationDeploymentContext ctx : this.deployed.values()) {
if (path.startsWith( ctx.getServletContext().getContextPath() ) && ctx.getServletContext().getContextPath().length() > match) {
match = ctx.getServletContext().getContextPath().length();
found = ctx;
}
}
return found;
}
/**
* Destroy this host, undeploying all the web applications.
*/
public void destroy () {
for (WebApplicationDeploymentContext context : new ArrayList<WebApplicationDeploymentContext>(deployed.values())) {
this.undeploy(context.getApplication().getName());
}
}
}