/******************************************************************************* * Copyright © 2012-2015 eBay Software Foundation * This program is dual licensed under the MIT and Apache 2.0 licenses. * Please see LICENSE for more information. *******************************************************************************/ package com.ebay.jetstream.application; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Options; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jmx.export.annotation.ManagedResource; import com.ebay.jetstream.config.Configuration; import com.ebay.jetstream.config.RootConfiguration; import com.ebay.jetstream.event.support.ShutDownOrchestrator; import com.ebay.jetstream.management.Management; import com.ebay.jetstream.util.CommonUtils; import com.ebay.jetstream.xmlser.Hidden; /** * @author shmurthy * * A container class for hosting Jetstream Event based applications. This class will provide a main method and * shutdown hook. It will start core services like messaging, monitoring and control, logging etc. It will load * the entry bean specified in configuration. The rest of the wiring will be driven by this entry bean. This * class can serve as a main for applications or as a collocated container. If this container's main is used, * then the following command line arguments must be specified Viz, appname, configfile name and config version. * If it is collocated with another container, these 3 parameters can be programatically set. * */ @ManagedResource(objectName = "Application") public class JetstreamApplication { private static class ShutdownHook extends Thread { @Override public void run() { try { getInstance().shutdown(); } catch (Throwable t) { throw CommonUtils.runtimeException(t); } System.out.println("Gracefully shutdown"); //KEEPME } } private static Class<? extends JetstreamApplication> s_applicationClass = JetstreamApplication.class; private static JetstreamApplication s_application; private static final Logger LOGGER = LoggerFactory.getLogger("com.ebay.jetstream.application"); private ThreadPoolExecutor m_worker; private BlockingQueue<Runnable> m_workQueue; protected static Class<? extends JetstreamApplication> getApplicationClass() { return s_applicationClass; } public static Configuration getConfiguration() { return RootConfiguration.getConfiguration(); } @Hidden public static JetstreamApplication getInstance() { if (s_application == null) { synchronized (s_applicationClass) { if (s_application == null) { try { s_applicationClass.newInstance(); } catch (Exception e) { throw CommonUtils.runtimeException(e); } } } } return s_application; } /** * Every Jetstream application shares a common main(). It creates the instance of the application, configures command * line options, parses the command line based on the options, starts the application based on the resulting * configuration, and then runs the application. * * @param args * command line arguments */ public static void main(String[] args) throws Exception { System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.Jdk14Logger"); JetstreamApplication ta = null; try { ta = getInstance(); // Allow JetstreamApplication option handling methods to be protected final JetstreamApplication optionHandler = ta; new CliOptions(new CliOptionHandler() { public Options addOptions(Options options) { return optionHandler.addOptions(options); } public void parseOptions(CommandLine line) { optionHandler.parseOptions(line); } }, args); if (System.getenv("COS") == null) System.setProperty("COS", "Dev"); ta.init(); } catch (Exception e) { LOGGER.error( "Failed to start Application" + e.getLocalizedMessage()); System.err.println("Failed to start application: " + e); e.printStackTrace(System.err); System.exit(1); } ta.run(); // this is the container's event loop } protected static void setApplicationClass(Class<? extends JetstreamApplication> applicationClass) { s_applicationClass = applicationClass; } private final Object m_monitor = new Object(); private final AtomicBoolean m_shutdown = new AtomicBoolean(false); private final JetstreamApplicationInformation m_applicationInformation = new JetstreamApplicationInformation(this); protected JetstreamApplication() { if (s_application != null) throw new IllegalStateException(s_application.getClass().getName() + " is already running"); s_application = this; RootConfiguration.applicationClass(m_applicationInformation, getClass()); Runtime.getRuntime().addShutdownHook(new ShutdownHook()); } /** * Adds options specific to this application. It may be overridden for custom application specific option * configuration. * * @param options * the Options to configure with new custom application command line options. * * @return the configured options. */ protected Options addOptions(Options options) { options.addOption("b", "beans", true, "Beans to start during initialization"); options.addOption("c", "config", true, "Configuration URL or file path"); options.addOption("cv", "configversion", true, "Version of configuration"); options.addOption("n", "name", true, "Name of application"); options.addOption("p", "port", true, "Monitoring port"); options.addOption("z", "zone", true, "URL or path of dns zone content"); options.addOption("nd", "nodns", false, "Not a network application"); options.addOption("wqz", "workqueuesz", true, "work queue size"); options.addOption("wt", "workerthreads", true, "worker threads"); return options; } public JetstreamApplicationInformation getApplicationInformation() { return m_applicationInformation; } /** * @param work * @return true if success else false */ public boolean submitWork(WorkRequest work) { if (m_workQueue != null) return m_workQueue.offer(work); return false; } /** * Override this method to do initialization in a custom JetstreamApplication. * * @throws Exception */ protected void init() throws Exception { JetstreamApplicationInformation ai = getApplicationInformation(); ai.selfLocate(); m_workQueue = new LinkedBlockingQueue<Runnable>(ai.getWorkQeueSz()); m_worker = new ThreadPoolExecutor(ai.getWorkerThreads(), 3, 30, TimeUnit.SECONDS, m_workQueue, new ThreadPoolExecutor.CallerRunsPolicy()); m_worker.prestartCoreThread(); Management.addBean(ai.getApplicationName(), this); logInfo("Starting services for " + ai); String[] configs = ai.getConfigRoots(); RootConfiguration rc = configs == null ? new RootConfiguration(ai) : new RootConfiguration(ai, configs); rc.start(); String[] sa = ai.getBeans(); if (sa != null) for (String bean : sa) rc.getBean(bean); } protected void logInfo(String message) { LOGGER.info( message); } protected void logSevereError(String message) { LOGGER.error( message); } /** * Parse the options given on the command line. This method may be overridden for application specific command line * option handling. * * @param commandLine * the parsed command line. */ protected void parseOptions(CommandLine commandLine) { JetstreamApplicationInformation ai = getApplicationInformation(); if (commandLine.hasOption('b')) { ai.setBeans(commandLine.getOptionValues('b')); } if (commandLine.hasOption('c')) { ai.setConfigRoots(commandLine.getOptionValues('c')); } if (commandLine.hasOption("cv")) { ai.setConfigVersion(commandLine.getOptionValue("cv")); } if (commandLine.hasOption('n')) { ai.setApplicationName(commandLine.getOptionValue('n')); } if (commandLine.hasOption('p')) { ai.setManagementPort(Integer.valueOf(commandLine.getOptionValue('p'))); System.setProperty("jetty_port",commandLine.getOptionValue('p')); } else{ System.setProperty("jetty_port" , String.valueOf(9999)); } if (commandLine.hasOption('z')) { ai.setZone(commandLine.getOptionValue('z')); } if (commandLine.hasOption("nd")) { ai.useDNS(false); } if (commandLine.hasOption("wqz")) { ai.setWorkQueueSz(Integer.valueOf(commandLine.getOptionValue("wqz"))); } if (commandLine.hasOption("wt")) { ai.setWorkerThreads(Integer.valueOf(commandLine.getOptionValue("wt"))); } } /** * Override this method to do work in the Jetstream application. */ protected void run() throws Exception { // Do nothing by default } public void shutdown() throws InterruptedException, Exception { try { if (ShutDownOrchestrator.getInstance() != null) { ShutDownOrchestrator.getInstance().shutDown(); } logInfo("Shutting down " + getApplicationInformation().getApplicationName()); } finally { Configuration c = getConfiguration(); if (c != null) c.close(); synchronized (m_monitor) { m_shutdown.set(true); m_monitor.notifyAll(); if (m_worker!=null) m_worker.shutdown(); } } } /** * shutdown shuts down all core services. It must be called for graceful exit, and is called automatically if * System.exit() is called. */ // 8853 fix - do not show this link no matter what @ManagedOperation protected void waitForShutdown() { while (!m_shutdown.get()) { System.out.println("waiting for shutdown"); //KEEPME synchronized (m_monitor) { try { m_monitor.wait(10000); } catch (InterruptedException e) { // Ignore } } } } }