package xapi.test.server;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.servlet.ServletContextHandler;
import xapi.bytecode.ClassFile;
import xapi.bytecode.annotation.Annotation;
import xapi.bytecode.annotation.StringMemberValue;
import xapi.dev.scanner.api.ClasspathScanner;
import xapi.dev.scanner.impl.ClasspathResourceMap;
import xapi.inject.X_Inject;
import xapi.log.X_Log;
import xapi.log.api.LogLevel;
import xapi.server.annotation.XapiServlet;
import xapi.time.X_Time;
import xapi.time.api.Moment;
import xapi.util.X_Namespace;
import xapi.util.X_Properties;
import xapi.util.X_Runtime;
import xapi.util.X_Util;
public class TestServer
{
Server server;
public static final int TEST_PORT = 13113;
private final int port = TEST_PORT;
public TestServer() {
X_Properties.setProperty(X_Namespace.PROPERTY_SERVER_PORT, Integer.toString(getPort()));
server = new Server(getPort());
// When testing, we don't want to setup configuration files,
// we want to write code and run-it-right-now.
// So, we scan the classpath for instances of HttpServlet,
// and we mount everything we find.
scanForServlets();
}
/**
* Performs runtime scan for servlets. For integration tests, you
* probably don't want your tests to load resources they can't load in production.
*/
@SuppressWarnings("unchecked")
protected void scanForServlets() {
if (X_Runtime.isRuntimeInjection()) {
final ClassLoader cl = getClassloader();
final ClasspathResourceMap resources = X_Inject.instance(ClasspathScanner.class)
.scanAnnotation(XapiServlet.class)
.matchClassFile(".*")
.scan(cl);
final boolean debug = X_Runtime.isDebug();
final ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/xapi");
context.setClassLoader(cl);
final Moment start = X_Time.now();
for (final ClassFile cls : resources.findClassAnnotatedWith(XapiServlet.class)) {
final Annotation a = cls.getAnnotation(XapiServlet.class.getName());
final StringMemberValue prefix = (StringMemberValue)a.getMemberValue("prefix");
if (debug) {
X_Log.info("Found XapiServlet "+cls.getName()+" mounted at /xapi/"+prefix.getValue());
}
String fragment = prefix.getValue();
if (fragment.length() == 0 || fragment.charAt(0) != '/') {
fragment = "/"+fragment;
}
// TODO: run injection on the servlet types
// TODO check if this servlet is registered in xapi.xml or not.
try {
context.addServlet(cls.getName(), fragment);
} catch (final Throwable e) {
X_Log.error("Error starting servlet "+cls.getName()+" @ "+fragment);
}
}
final ContextHandlerCollection all = new ContextHandlerCollection();
all.addHandler(context);
all.addHandler(new DefaultHandler());
server.setHandler(all);
if (X_Log.loggable(LogLevel.DEBUG)) {
X_Log.trace("Scanned XApiServlet annotations in "+X_Time.difference(start)+" ");
}
}
}
protected ClassLoader getClassloader() {
return Thread.currentThread().getContextClassLoader();
}
protected int getPort() {
return port;
}
/**
* Called when the server is ready.
*/
protected void onReady() {
}
public void start() {
if (server.isStarted()) {
return;
}
try {
server.start();
} catch (final Exception e) {
X_Log.error("Test server could not start", e);
throw X_Util.rethrow(e);
}
}
public void finish() {
try {
server.stop();
} catch (final Exception e) {
X_Log.warn("Failure stopping server: ", e);
Thread.currentThread().interrupt();
} finally {
server.destroy();
}
}
}