package com.collabinate.server; import java.io.Console; import java.io.File; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.DefaultConfigurationBuilder; import org.restlet.*; import org.restlet.data.ChallengeScheme; import org.restlet.security.ChallengeAuthenticator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.collabinate.server.engine.CollabinateAdmin; import com.collabinate.server.engine.CollabinateGraph; import com.collabinate.server.engine.CollabinateReader; import com.collabinate.server.engine.CollabinateWriter; import com.collabinate.server.engine.GraphAdmin; import com.collabinate.server.engine.GraphEngine; import com.collabinate.server.webserver.CollabinateComponent; import com.collabinate.server.webserver.CollabinateVerifier; import com.tinkerpop.blueprints.Graph; import com.tinkerpop.blueprints.GraphFactory; import com.tinkerpop.blueprints.KeyIndexableGraph; /** * Main Collabinate Server entry point. * */ public class Collabinate { private static Configuration configuration; private static CollabinateGraph graph; private static CollabinateReader reader; private static CollabinateWriter writer; private static CollabinateAdmin admin; private static Restlet webServer; static { // Set up log4j2 logging before logger is created. System.setProperty("log4j.configurationFile", "log4j2.xml"); System.setProperty("Log4jContextSelector", "org.apache.logging.log4j.core.async.AsyncLoggerContextSelector"); } /** * Static logger for main application. */ private static final Logger logger = LoggerFactory.getLogger(Collabinate.class); public static void main(String[] args) throws Exception { // track server startup time long startTime = System.currentTimeMillis(); // output non-logged startup message System.out.println("Collabinate Server starting..."); // set up logging System.setProperty("org.restlet.engine.loggerFacadeClass", "org.restlet.ext.slf4j.Slf4jLoggerFacade"); // load configuration String version = getConfiguration().getString( "collabinate.version", "Unknown"); String build = getConfiguration().getString("collabinate.build", ""); logger.info("Collabinate Server version {}{}", version, build.equals("") ? "" : ("+" + build)); // connect to the data store connectGraphDatabase(); registerShutdownHook(); // set up the back end engine createEngine(); // start the web server that handles service requests initializeWeb(); // output server startup time long totalStartTime = System.currentTimeMillis() - startTime; logger.info(String.format( "Collabinate Server started in %1$d milliseconds", totalStartTime)); enableConsoleQuit(); } /** * Performs a complete recycle of the system, including shutdown and * recreation of the web server and the database reference. This is included * as a stopgap for unknown leaks or infrastructure issues. */ public static void resetService() throws Exception { logger.info("Service resetting..."); // stop and destroy the web server, engine, and graph webServer.stop(); webServer = null; reader = null; writer = null; admin = null; graph.shutdown(); graph = null; // recreate and restart the graph, engine, and web server connectGraphDatabase(); createEngine(); initializeWeb(); logger.info("Service reset."); } /** * Opens a connection to the configured graph database. */ private static void connectGraphDatabase() { if (null != graph) throw new IllegalStateException("Graph already connected"); Graph configuredGraph = GraphFactory.open("graph.properties"); if (!(configuredGraph instanceof KeyIndexableGraph)) throw new IllegalStateException( "Configured graph is not a KeyIndexableGraph"); graph = new CollabinateGraph( (KeyIndexableGraph)configuredGraph); } /** * Creates the Collabinate engine for data handling. */ private static void createEngine() { GraphEngine engine = new GraphEngine(graph); reader = engine; writer = engine; admin = new GraphAdmin(graph); } /** * Sets up the Restlet service that is the Collabinate front end. * * @throws Exception */ private static void initializeWeb() throws Exception { if (null != webServer && webServer.isStarted()) throw new IllegalStateException("Web server is already started"); // create the authenticator ChallengeAuthenticator authenticator = new ChallengeAuthenticator( null, // context gets added in the component false, // authentication is not optional ChallengeScheme.HTTP_BASIC, "Collabinate", new CollabinateVerifier(admin)); // create the Restlet component and start it webServer = new CollabinateComponent(reader, writer, admin, authenticator); webServer.start(); } /** * Sets up the service for console termination, if a console is available. * * @throws Exception */ private static void enableConsoleQuit() throws Exception { Console console = System.console(); if (null != console) { // if the system can be stopped via the console, let the user know System.out.println("Press Enter to quit"); System.console().readLine(); logger.info("Collabinate Server shutting down..."); webServer.stop(); } else { System.out.println("No interactive console available;" + " terminate process to quit"); } } /** * Retrieves the loaded configuration for the server. * * @return The server configuration. * @throws ConfigurationException */ public static Configuration getConfiguration() { if (null != configuration) return configuration; DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder(); builder.setFile(new File("configDefinition.xml")); try { configuration = builder.getConfiguration(); } catch (ConfigurationException exc) { throw new IllegalStateException("Problem loading config", exc); } if (null == configuration) throw new IllegalStateException("Configuration not loaded"); return configuration; } /** * Registers a shutdown hook for clean graph shutdown. * * @param graph The graph that needs clean shutdown upon system shutdown. */ private static void registerShutdownHook() { Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { graph.shutdown(); System.out.println("Collabinate Server shutdown complete."); } }); } }