package org.geowebcache.jetty;
import java.io.File;
import java.net.BindException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Objects;
import java.util.function.Consumer;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.mortbay.jetty.Connector;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.bio.SocketConnector;
import org.mortbay.jetty.webapp.WebAppContext;
import org.mortbay.thread.QueuedThreadPool;
/**
* Rule that runs a live GWC instance inside of Jetty for integration tests
*
* @author Kevin Smith, Boundless
*
*/
public class JettyRule extends org.junit.rules.ExternalResource {
Integer port = null;
TemporaryFolder temp = new TemporaryFolder();
Server jettyServer;
private File confDir;
private File cacheDir;
private File workDir;
public int getPort() {
return getServer().getConnectors()[0].getLocalPort();
}
Initializer<File> confInit;
Initializer<File> cacheInit;
public JettyRule() {
this(d -> {}, d -> {});
}
public JettyRule(Initializer<File> confInit, Initializer<File> cacheInit) {
super();
this.confInit = confInit;
this.cacheInit = cacheInit;
}
public interface Initializer<T> {
public void accept(T toInit) throws Exception;
default Initializer<T> andThen(Consumer<? super T> afterInit) {
Objects.requireNonNull(afterInit);
return (T toInit) -> {
accept(toInit);
afterInit.accept(toInit);
};
}
default Consumer<T> makeSafe(Consumer<Exception> handler) {
return (T toInit) -> {
try {
accept(toInit);
} catch (Exception e) {
handler.accept(e);
}
};
}
}
@Override
public Statement apply(Statement base, Description description) {
// This rule goes inside the TemporaryFolder rule.
return temp.apply(super.apply(base, description), description);
}
public Server getServer() {
if(jettyServer==null) {
throw new IllegalStateException();
}
return jettyServer;
}
public URI getUri() {
try {
return new URI("http",null,"localhost", getPort(), "/geowebcache/", null, null);
} catch (URISyntaxException e) {
throw new IllegalStateException(e);
}
}
@Override
protected void before() throws Exception {
confDir = temp.newFolder("conf");
cacheDir = temp.newFolder("cache");
workDir = temp.newFolder("work");
confInit.accept(confDir);
cacheInit.accept(cacheDir);
jettyServer = new Server();
try {
SocketConnector conn = new SocketConnector();
int port;
for(port=8080; port<=8180; port++) {
conn.setPort(port);
try{
conn.open();
} catch (BindException e) {
continue;
}
}
conn.setPort(port);
conn.setAcceptQueueSize(100);
jettyServer.setConnectors(new Connector[] { conn });
WebAppContext wah = new WebAppContext();
wah.setContextPath("/geowebcache");
wah.setWar("src/main/webapp");
//wah.setResourceAlias(alias, uri); // Use this to replace the spring context files?
wah.getInitParams().put("GEOWEBCACHE_CONF_DIR", confDir.getCanonicalPath());
wah.getInitParams().put("GEOWEBCACHE_CACHE_DIR", cacheDir.getCanonicalPath());
jettyServer.setHandler(wah);
wah.setTempDirectory(workDir);
// Use this to set a limit on the number of threads used to respond requests
QueuedThreadPool tp = new QueuedThreadPool();
tp.setMinThreads(50);
tp.setMaxThreads(50);
conn.setThreadPool(tp);
jettyServer.start();
} catch (Exception e) {
if (jettyServer != null) {
try {
jettyServer.stop();
} catch (Exception e1) {
e1.addSuppressed(e);
throw e1;
}
}
throw e;
}
}
@Override
protected void after() {
// Jetty shutdown takes 100 seconds so do this in a thread to speed it up.
new Thread(()->{
try {
jettyServer.stop();
} catch (Exception e) {
throw new IllegalStateException("Error while shutting down test Jetty",e);
}
}).start();
}
}