package org.jboss.seam.wicket.mock;
import java.io.File;
import java.net.URISyntaxException;
import java.net.URL;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletResponse;
import org.apache.wicket.protocol.http.HttpSessionStore;
import org.apache.wicket.protocol.http.WebRequestCycle;
import org.apache.wicket.protocol.http.WebResponse;
import org.apache.wicket.session.ISessionStore;
import org.apache.wicket.util.tester.BaseWicketTester;
import org.apache.wicket.util.tester.DummyHomePage;
import org.apache.wicket.util.tester.WicketTester;
import org.jboss.seam.Seam;
import org.jboss.seam.contexts.Lifecycle;
import org.jboss.seam.contexts.ServletLifecycle;
import org.jboss.seam.core.Init;
import org.jboss.seam.init.Initialization;
import org.jboss.seam.mock.EmbeddedBootstrap;
import org.jboss.seam.wicket.SeamWebApplication;
/**
* A subclass of WicketTester that enforces the use of a SeamWebApplication. This ensures that
* ensures that seam contexts are are set up and torn down correctly in testing. Each SeamWebApplication
* gets a complete application context, so seam application teardown/restart happens if you create
* a new SeamWicketTester without propogating any existing SeamWebApplication through the constructor.
*/
public class SeamWicketTester extends WicketTester
{
public SeamWicketTester()
{
this(DummyHomePage.class);
}
public SeamWicketTester(Class homePage)
{
this(homePage,null);
}
public SeamWicketTester(Class homePage, Class loginPage)
{
this(createApplication(homePage, loginPage));
}
public SeamWicketTester(final SeamWebApplication application)
{
this(application,null);
}
public SeamWicketTester(final SeamWebApplication application, final String path)
{
super(application,path);
/*
* When in tests, we want the contexts destroyed lazily, i.e.
* don't destroy the contexts after a request, but rather before the
* next request, so that we can inspect the values of wicket components and
* their models
*/
Lifecycle.beginCall();
((SeamWebApplication)getApplication()).setDestroyContextsLazily(true);
Lifecycle.endCall();
}
/**
* Create a SeamWebApplication suitable for testing, a la WicketTester's DummyApplication
* @param homePage The WebPage class to start on
* @param loginPage The WebPage class to use for any Seam Authentication redirects
*/
private static SeamWebApplication createApplication(final Class homePage, final Class loginPage)
{
return new SeamWebApplication()
{
@Override
public Class getHomePage()
{
return homePage;
}
@Override
protected Class getLoginPage()
{
return loginPage;
}
@Override
protected ISessionStore newSessionStore()
{
// Don't use a filestore, or we spawn lots of threads, which
// makes things slow.
return new HttpSessionStore(this);
}
@Override
protected WebResponse newWebResponse(final HttpServletResponse servletResponse)
{
// Don't use a buffered response in testing
return new WebResponse(servletResponse);
}
@Override
protected void outputDevelopmentModeWarning()
{
// Do nothing.
}
};
}
/**
* For each new servletContext, i.e. each new SeamWebApplication instance, we create a new seam
* initialization.
*/
@Override
public ServletContext newServletContext(String path)
{
if (ServletLifecycle.getServletContext() != null)
{
ServletLifecycle.endApplication();
}
if (path == null)
{
URL webxml = getClass().getResource("/WEB-INF/web.xml");
if (webxml != null)
{
try
{
path = new File(webxml.toURI()).getParentFile().getParentFile().getAbsolutePath();
}
catch (URISyntaxException e)
{
throw new RuntimeException(e);
}
}
}
ServletContext context = super.newServletContext(path);
startJbossEmbeddedIfNecessary();
ServletLifecycle.beginApplication(context);
new Initialization(context).create().init();
((Init) context.getAttribute(Seam.getComponentName(Init.class))).setDebug(false);
return context;
}
@Override
public WebRequestCycle setupRequestAndResponse(boolean isAjax) {
WebRequestCycle cycle = super.setupRequestAndResponse(isAjax);
/**
* FormTester wants to walk the form tree and call getValue() on components
* without having started the request cycle. This fails when wicket components have
* seam injections. So force a call here
*/
BaseWicketTester.callOnBeginRequest(cycle);
return cycle;
}
private static boolean started;
protected void startJbossEmbeddedIfNecessary()
{
try
{
if (!started && embeddedJBossAvailable())
{
new EmbeddedBootstrap().startAndDeployResources();
}
started = true;
}
catch (Exception exception)
{
throw new RuntimeException("Failure starting up Embedded Jboss",exception);
}
}
private boolean embeddedJBossAvailable()
{
try
{
Class.forName("org.jboss.embedded.Bootstrap");
return true;
}
catch (ClassNotFoundException e)
{
return false;
}
}
}