package com.xenoage.zong.webserver;
import com.google.gson.Gson;
import com.xenoage.utils.Parser;
import com.xenoage.utils.error.BasicErrorProcessing;
import com.xenoage.utils.error.Err;
import com.xenoage.utils.jse.log.DesktopLogProcessing;
import com.xenoage.utils.jse.settings.Settings;
import com.xenoage.utils.log.Log;
import com.xenoage.zong.Zong;
import com.xenoage.zong.desktop.io.midi.out.SynthManager;
import com.xenoage.zong.desktop.utils.JseZongPlatformUtils;
import com.xenoage.zong.webserver.init.DBInit;
import com.xenoage.zong.webserver.servlet.ActionServlet;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.server.handler.ResourceHandler;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import javax.servlet.ServletException;
import javax.sound.midi.MidiUnavailableException;
import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import static com.xenoage.utils.error.Err.handle;
import static com.xenoage.utils.log.Log.log;
import static com.xenoage.utils.log.Report.fatal;
import static com.xenoage.utils.log.Report.remark;
/**
* Main class of the Webviewer server.
*
* @author Andreas Wenger
*/
public class Webserver {
public static final String projectName = "Webserver";
public static final String filename = Zong.filename + "/" + projectName.toLowerCase() + "/";
public static final String webPath = "data/web/";
public static Webserver instance = null;
public static int port = 8080;
private org.eclipse.jetty.server.Server server;
private List<Handler> handlers = new ArrayList<>();
private Connection dbConnection = null;
private Gson gson = null;
public static void main(String... args) {
instance = new Webserver();
instance.start();
try {
instance.join();
} catch (InterruptedException ex) {
Log.log(remark("Server interrupted."));
}
}
public Webserver() {
//init IO and logging
JseZongPlatformUtils.init(filename);
Log.init(new DesktopLogProcessing(Zong.getNameAndVersion(projectName)));
Err.init(new BasicErrorProcessing());
//load settings
Settings.setErrorProcessing(null);
Settings.getInstance();
//prepare gson
gson = new Gson();
//open the database
try {
openDBConnection();
} catch (Exception ex) {
handle(fatal("Could not open DB connection", ex));
}
//init audio engine
try {
SynthManager.init(true);
} catch (MidiUnavailableException ex) {
handle(fatal("Could not init audio engine", ex));
}
//server
port = Parser.parseInt(getSetting("port"), 8080);
this.server = new org.eclipse.jetty.server.Server(port);
//TODO: set threadpool size - this.server.setThreadPool(new QueuedThreadPool(10));
ServletContextHandler servletHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
servletHandler.setContextPath("/action");
servletHandler.addServlet(new ServletHolder(new ActionServlet()), "/");
handlers.add(servletHandler);
//enable webserver
enableWebServer();
}
/**
* Starts the server.
*/
public void start() {
try {
//Handler setzen
HandlerList handlerList = new HandlerList();
for (Handler handler : handlers)
handlerList.addHandler(handler);
handlerList.addHandler(new DefaultHandler());
server.setHandler(handlerList);
//Server starten
server.start();
String log = "Server started. Listening on port " + port + ".";
System.out.println(log);
log(remark(log));
} catch (Exception ex) {
handle(fatal(ex));
}
}
/**
* Blocks the server's thread as long as the server is running.
*/
public void join()
throws InterruptedException {
server.join();
}
/**
* Stops the server.
*/
public void stop() {
try {
server.stop();
} catch (Exception ex) {
handle(fatal(ex));
}
}
private void enableWebServer() {
if (server.isRunning())
throw new IllegalStateException("Webserver can not be enabled while running");
ResourceHandler resourceHandler = new ResourceHandler();
resourceHandler.setDirectoriesListed(true);
resourceHandler.setWelcomeFiles(new String[] { "index.html" });
resourceHandler.setResourceBase(webPath);
resourceHandler.setCacheControl("max-age=600,public"); //cache 10 minutes
ContextHandler contentHandler = new ContextHandler();
contentHandler.setContextPath("/");
contentHandler.setResourceBase(".");
contentHandler.setClassLoader(Thread.currentThread().getContextClassLoader());
contentHandler.setHandler(resourceHandler);
handlers.add(contentHandler);
}
private void openDBConnection()
throws Exception {
try {
//new File("data/db/" + getSetting("dbdatabase") + ".h2.db").delete(); //TEST
//new database?
boolean newDB = !(new File("data/db/" + getSetting("dbdatabase") + ".h2.db").exists());
//open database
Log.log(remark("Open connection to database..."));
Class.forName("org.h2.Driver");
dbConnection = DriverManager.getConnection("jdbc:h2:data/db/" + getSetting("dbdatabase"),
getSetting("dbuser"), getSetting("dbpassword"));
Log.log(remark("Connection established"));
//init
if (newDB)
DBInit.initDatabase(dbConnection);
} catch (ClassNotFoundException ex) {
handle(fatal("Could not find JDBC driver for MySQL", ex));
throw new ServletException("See log file");
} catch (SQLException ex) {
handle(fatal("SQLException: " + ex.getMessage(), ex));
throw new ServletException("See log file");
}
}
/**
* Gets the database connection. If it is closed, it is re-established.
*/
public Connection getDBConnection() {
try {
if (dbConnection.isClosed()) {
Log.log(remark("Database connection is closed. Trying to reopen the connection..."));
openDBConnection();
}
} catch (Exception ex) {
handle(fatal("Exception: " + ex.getMessage(), ex));
throw new RuntimeException(ex);
}
return dbConnection;
}
public Gson getGson() {
return gson;
}
public String getSetting(String key) {
String ret = Settings.getInstance().getSetting(key, "webviewer");
if (ret == null)
throw new RuntimeException("Missing config value for: " + key);
return ret;
}
}