package me.moodcat.core; import com.google.inject.Injector; import com.google.inject.Module; import lombok.AccessLevel; import lombok.Getter; import java.io.File; import java.io.IOException; import java.util.concurrent.atomic.AtomicReference; import lombok.extern.slf4j.Slf4j; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.server.handler.ResourceHandler; import org.eclipse.jetty.server.session.HashSessionIdManager; import org.eclipse.jetty.server.session.HashSessionManager; import org.eclipse.jetty.util.resource.Resource; import org.slf4j.bridge.SLF4JBridgeHandler; /** * Main entry-point for the backend server. Initializes all {@link me.moodcat.api APIs}, starts the * {@link #server} and connects to the database. */ @Slf4j public class App { /** * The time that sessions are kept in the cache. */ private static final int SESSION_KEEP_ALIVE = 1800; /** * Default TCP port. */ private static final int SERVER_PORT = 8080; /** * The server that handles the requests. */ @Getter(value = AccessLevel.PACKAGE) private final Server server; /** * Reference for injector in order to have concurrent transactions to our database. */ @Getter(value = AccessLevel.PACKAGE) private final AtomicReference<Injector> injectorAtomicReference = new AtomicReference<>(); /** * Instantiates the server and adds handlers for the requests. * * @throws IOException * If the statics folder threw an IOException. */ public App(final Module... overrides) throws IOException { final File staticsFolder = new File("src/main/resources/static/app"); // Make sure the folder is available, else we can't start the server. if (!staticsFolder.exists() && !staticsFolder.mkdir()) { throw new IOException("Static folder could not be initialized."); } for (final String file : staticsFolder.list()) { log.info("Found resource {}", file); } this.server = new Server(SERVER_PORT); this.server.setSessionIdManager(new HashSessionIdManager()); this.server.setHandler(this.attachHandlers(staticsFolder, overrides)); } /** * Starts the server and waits for the server to shut down. * * @param args * None. * @throws Exception * Thrown when a thread-exception occured. */ public static void main(final String... args) throws Exception { SLF4JBridgeHandler.removeHandlersForRootLogger(); SLF4JBridgeHandler.install(); final App app = new App(); app.startServer(); app.joinThread(); } private ContextHandlerCollection attachHandlers(final File staticsFolder, final Module... overrides) { final MoodcatHandler moodcatHandler = new MoodcatHandler(this, staticsFolder, overrides); final ResourceHandler resources = new ResourceHandler(); resources.setBaseResource(Resource.newResource(staticsFolder)); resources.setDirectoriesListed(false); resources.setCacheControl("max-age=3600"); final HashSessionManager hashSessionManager = new HashSessionManager(); hashSessionManager.setMaxInactiveInterval(SESSION_KEEP_ALIVE); final ContextHandlerCollection handlers = new ContextHandlerCollection(); // CHECKSTYLE:OFF handlers.addContext("/", "/").setHandler(resources); handlers.addContext("/", "/").setHandler(moodcatHandler); // CHECKSTYLE:ON return handlers; } /** * Starts the {@link App} server. * * @throws Exception * In case the server could not be started. */ public void startServer() throws Exception { this.server.start(); Runtime.getRuntime().addShutdownHook(new Thread(this::stopServer)); } /** * Joins the {@link App} server. * * @throws InterruptedException * if the joined thread is interrupted * before or during the merging. */ public void joinThread() throws InterruptedException { this.server.join(); } /** * Get the injector. * * @return the injector */ public Injector getInjector() { return injectorAtomicReference.get(); } /** * Stops the {@link App} server. */ public void stopServer() { try { this.server.stop(); } catch (final Exception e) { log.warn(e.getMessage(), e); } } }