package com.urbanairship.octobot; import java.util.List; import java.util.HashMap; import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; import org.apache.log4j.BasicConfigurator; // The fun starts here! // This class is the main entry point to the application. // It initializes (a) queue consumer thread(s) reponsible for // receiving and passing messages on to tasks for execution. public class Octobot { private static final Logger logger = Logger.getLogger("Octobot"); public static void main(String[] args) { // Initialize logging from a log4j configuration file. String configFile = System.getProperty("log4j.configuration"); if (configFile != null && !configFile.equals("")) { PropertyConfigurator.configure(configFile); } else { BasicConfigurator.configure(); logger.warn("log4j.configuration not set - logging to stdout."); } // Force settings to initialize before loading application components. Settings.get(); // If a startup hook is configured, call it before launching workers. String startupHook = Settings.get("Octobot", "startup_hook"); if (startupHook != null && !startupHook.equals("")) launchStartupHook(startupHook); // If a shutdown hook is configured, register it. String shutdownHook = Settings.get("Octobot", "shutdown_hook"); if (shutdownHook != null && !shutdownHook.equals("")) registerShutdownHook(shutdownHook); boolean enableEmailErrors = Settings.getAsBoolean("Octobot", "email_enabled"); if (enableEmailErrors) { logger.info("Launching email notification queue..."); new Thread(MailQueue.get(), "Email Queue").start(); } logger.info("Launching Introspector..."); new Thread(new Introspector(), "Introspector").start(); logger.info("Launching Workers..."); List<HashMap<String, Object>> queues = null; try { queues = getQueues(); } catch (NullPointerException e) { logger.fatal("Error: No valid queues found in Settings. Exiting."); throw new Error("Error: No valid queues found in Settings. Exiting."); } // Start a thread for each queue Octobot is configured to listen on. for (HashMap<String, Object> queueConf : queues) { // Fetch the number of workers to spawn and their priority. int numWorkers = Settings.getIntFromYML(queueConf.get("workers"), 1); int priority = Settings.getIntFromYML(queueConf.get("priority"), 5); Queue queue = new Queue(queueConf); // Spawn worker threads for each queue in our configuration. for (int i = 0; i < numWorkers; i++) { QueueConsumer consumer = new QueueConsumer(queue); Thread worker = new Thread(consumer, "Worker"); logger.info("Attempting to connect to " + queueConf.get("protocol") + " queue: " + queueConf.get("name") + " with priority " + priority + "/10 " + "(Worker " + (i+1) + "/" + numWorkers + ")."); worker.setPriority(priority); worker.start(); } } logger.info("Octobot ready to rock!"); } // Invokes a startup hook registered from the YML config on launch. private static void launchStartupHook(String className) { logger.info("Calling Startup Hook: " + className); try { Class<?> startupHook = Class.forName(className); Method method = startupHook.getMethod("run", (Class[]) null); method.invoke(startupHook.newInstance(), (Object[]) null); } catch (ClassNotFoundException e) { logger.error("Could not find class: " + className + " for the " + "startup hook specified. Please ensure that it exists in your" + " classpath and launch Octobot again. Continuing without" + " executing this hook..."); } catch (NoSuchMethodException e) { logger.error("Your startup hook: " + className + " does not " + " properly implement the Runnable interface. Your startup hook must " + " contain a method with the signature: public void run()." + " Continuing without executing this hook..."); } catch (InvocationTargetException e) { logger.error("Your startup hook: " + className + " caused an error" + " in execution. Please correct this error and re-launch Octobot." + " Continuing without executing this hook...", e.getCause()); } catch (Exception e) { logger.error("Your startup hook: " + className + " caused an unknown" + " error. Please see the following stacktrace for information.", e); } } // Registers a Runnable to be ran as a shutdown hook when Octobot stops. private static void registerShutdownHook(String className) { logger.info("Registering Shutdown Hook: " + className); try { Class startupHook = Class.forName(className); Runtime.getRuntime().addShutdownHook(new Thread((Runnable) startupHook.newInstance())); } catch (ClassNotFoundException e) { logger.error("Could not find class: " + className + " for the " + "shutdown hook specified. Please ensure that it exists in your" + " classpath and launch Octobot again. Continuing without" + " registering this hook..."); } catch (ClassCastException e) { logger.error("Your shutdown hook: " + className + " could not be " + "registered due because it does not implement the Runnable " + "interface. Continuing without registering this hook..."); } catch (Exception e) { logger.error("Your shutdown hook: " + className + " could not be " + "registered due to an unknown error. Please see the " + "following stacktrace for debugging information.", e); } } @SuppressWarnings("unchecked") private static List<HashMap<String, Object>> getQueues() { return (List<HashMap<String, Object>>) Settings.configuration.get("Octobot").get("queues"); } }