package org.jano;
import fi.iki.elonen.NanoHTTPD;
import fi.iki.elonen.NanoHTTPD.Response;
import fi.iki.elonen.NanoHTTPD.Response.Status;
import org.jano.resources.ResourceRef;
import org.jano.util.StringUtils;
import org.jano.zuss.ZussPlugin;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* JANO (Java Nano web server)
* This is lightweight server-logic-less web server with ability to update/persist some resource content.
* Features:
* + no servlet container required
* + mount-based virtual resource system with access rights
* + less-like CSS preprocessing
* + ability to push back resource content
*/
public class JanoServer {
private final NanoHTTPD httpd;
private final ExecutorService requestExecutor = Executors.newCachedThreadPool();
private final VirtualResourceSystem resourceSystem;
private final List<JanoPlugin> plugins = new ArrayList<JanoPlugin>();
protected JanoServer(JanoConfig config) {
httpd = new NanoHTTPD(config.hostname(), config.port()) {
@Override
public Response serve(IHTTPSession session) {
return JanoServer.this.serve(session);
}
};
//consider to use executor service rather than default new Thread().start()
httpd.setAsyncRunner(new NanoHTTPD.AsyncRunner() {
@Override
public void exec(Runnable code) {
requestExecutor.submit(code);
}
});
//creating virtual system based on mount points
resourceSystem = new VirtualResourceSystem(config.mountPoints());
plugins.add(new IndexPlugin());
plugins.add(new ZussPlugin());
plugins.add(new ContentPlugin());
}
/**
* Non-blocking operation to start server
*/
public JanoServer start() {
try {
httpd.start();
Diagnostic.log("Server is started!");
Diagnostic.log("--------------------------------------------------------------------------------------------");
} catch (Exception e) {
throw new JanoException("Unable to start Jano server", e);
}
return this;
}
/**
* Blocking operation to await server is stopped
*/
public JanoServer await() {
while (httpd.isAlive()) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
break;
}
}
return this;
}
/**
* Stops web server. Blocked until thread is not stopped and socket is closed
*/
public JanoServer stop() {
httpd.stop();
return this;
}
/**
* The main entry point of server - processor of any request
*/
protected NanoHTTPD.Response serve(NanoHTTPD.IHTTPSession session) {
Response response = serveResource(session);
Diagnostic.log("\tStatus." + response.getStatus() + " (" + response.getMimeType() + ")");
return response;
}
protected NanoHTTPD.Response serveResource(NanoHTTPD.IHTTPSession session) {
try {
String uri = session.getUri();
ResourceRef ref = resourceSystem.get(uri);
Diagnostic.log(session.getMethod() + " " + StringUtils.pad(uri, 32) + " " + ref);
try {
for (JanoPlugin plugin : plugins) {
if (plugin.canServe(uri, session.getMethod())) {
return plugin.serve(session, resourceSystem);
}
}
return Reply.with(Status.NO_CONTENT, ref);
} catch (SecurityException e) {
Diagnostic.log(e);
return Reply.with(Status.FORBIDDEN, ref);
}
} catch (Exception e) {
Diagnostic.log(e);
return new NanoHTTPD.Response(Status.INTERNAL_ERROR, MimeType.TEXT, e.getMessage());
}
}
}