package org.marketcetera.core; import java.io.File; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.commons.lang.StringUtils; import org.apache.log4j.PropertyConfigurator; import org.marketcetera.util.log.SLF4JLoggerProxy; import org.marketcetera.util.misc.ClassVersion; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.Lifecycle; import org.springframework.context.support.FileSystemXmlApplicationContext; /* $License$ */ /** * Provides a process-based application in which to run Marketcetera components. * * @author <a href="mailto:colin@marketcetera.com">Colin DuPlantis</a> * @version $Id: ApplicationContainer.java 16901 2014-05-11 16:14:11Z colin $ * @since 2.4.0 */ @ClassVersion("$Id: ApplicationContainer.java 16901 2014-05-11 16:14:11Z colin $") public class ApplicationContainer extends ApplicationBase implements ApplicationInfoProvider, Lifecycle { /* (non-Javadoc) * @see org.marketcetera.core.ApplicationInfoProvider#getAppDir() */ @Override public File getAppDir() { return new File(APP_DIR); } /* (non-Javadoc) * @see org.marketcetera.core.ApplicationInfoProvider#getConfDir() */ @Override public File getConfDir() { return new File(CONF_DIR); } /* (non-Javadoc) * @see org.marketcetera.core.ApplicationInfoProvider#getContext() */ @Override public ConfigurableApplicationContext getContext() { return context; } /* (non-Javadoc) * @see org.marketcetera.core.ApplicationInfoProvider#getArguments() */ @Override public String[] getArguments() { return arguments; } /** * Sets the application arguments value. * * @param inArgs a <code>String[]</code> value */ public void setArguments(String...inArgs) { arguments = inArgs; } /** * Starts application. * * @param args a <code>String[]</code> value */ public static void main(String[] args) { // configure logger PropertyConfigurator.configureAndWatch(ApplicationBase.CONF_DIR+"log4j.properties", LOGGER_WATCH_DELAY); // log application start Messages.APP_COPYRIGHT.info(ApplicationContainer.class); Messages.APP_VERSION_BUILD.info(ApplicationContainer.class, ApplicationVersion.getVersion(ApplicationContainer.class), ApplicationVersion.getBuildNumber()); Messages.APP_START.info(ApplicationContainer.class); // check to see if we're using a different starting context file than the default String rawContextFilename = StringUtils.trimToNull(System.getProperty(CONTEXT_FILE_PROP)); if(rawContextFilename != null) { contextFilename = rawContextFilename; } final ApplicationContainer application; try { application = new ApplicationContainer(); application.setArguments(args); application.start(); } catch(Exception e) { e.printStackTrace(); try { Messages.APP_STOP_ERROR.error(ApplicationContainer.class, e); } catch(Exception e2) { System.err.println("Reporting failed"); //$NON-NLS-1$ System.err.println("Reporting failure"); //$NON-NLS-1$ e2.printStackTrace(); System.err.println("Original failure"); //$NON-NLS-1$ e.printStackTrace(); } System.exit(exitCode); return; } Messages.APP_STARTED.info(ApplicationContainer.class); Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { application.stop(); Messages.APP_STOP.info(ApplicationContainer.class); } }); try { application.startWaitingForever(); } catch(Exception e) { try { Messages.APP_STOP_ERROR.error(ApplicationContainer.class, e); } catch(Exception e2) { System.err.println("Reporting failed"); //$NON-NLS-1$ System.err.println("Reporting failure"); //$NON-NLS-1$ e2.printStackTrace(); System.err.println("Original failure"); //$NON-NLS-1$ e.printStackTrace(); } System.exit(exitCode); return; } Messages.APP_STOP_SUCCESS.info(ApplicationContainer.class); System.exit(exitCode); } /** * Adds the given shutdown task to the shutdown task collection. * * <p>Tasks are executed upon application shutdown in their native order. * * @param inTask a <code>ShutdownTask</code> value */ public synchronized static void addShutdownTask(ComparableTask inTask) { shutdownTasks.add(inTask); } /** * Removes the given shutdown task from the shutdown task collection. * * @param inTask a <code>ShutdownTask</code> value */ public synchronized static void removeShutdownTask(ComparableTask inTask) { shutdownTasks.remove(inTask); } /** * Stops the running instance from waiting. * * <p>Does not stop the instance, just interrupts the running loop. */ public static void stopInstanceWaiting() { if(instance != null) { instance.stopWaitingForever(); } } /** * Gets the arguments passed to the application if any. * * @return a <code>String[]</code> value or <code>null</code> */ public static String[] getInstanceArguments() { if(instance == null) { return null; } return instance.getArguments(); } /** * Get the instance value. * * @return an <code>ApplicationContainer</code> value */ public static ApplicationContainer getInstance() { return instance; } /* (non-Javadoc) * @see org.springframework.context.Lifecycle#isRunning() */ @Override public boolean isRunning() { return running.get(); } /* (non-Javadoc) * @see org.springframework.context.Lifecycle#start() */ @Override public synchronized void start() { instance = this; context = generateContext(); context.registerShutdownHook(); running.set(true); } /* (non-Javadoc) * @see org.springframework.context.Lifecycle#stop() */ @Override public synchronized void stop() { try { for(ComparableTask task : shutdownTasks) { try { task.run(); } catch (Exception e) { SLF4JLoggerProxy.warn(this, e, "Error executing shutdown task: {}", task); } } context.stop(); context = null; } finally { running.set(false); } } /** * Sets the exit code that will be returned when the application quits. * * @param inExitCode an <code>int</code> value */ public static void setExitCode(int inExitCode) { exitCode = inExitCode; } /** * Get the exitCode value. * * @return an <code>int</code> value */ public static int getExitCode() { return exitCode; } /** * Generates the base application context with which to run. * * @return a <code>ConfigurableApplicationContext</code> value */ protected ConfigurableApplicationContext generateContext() { return new FileSystemXmlApplicationContext(new String[] { "file:"+CONF_DIR+contextFilename }, //$NON-NLS-1$ null); } /** * indicates if the app is running or not */ private final AtomicBoolean running = new AtomicBoolean(false); /** * arguments passed to the cmd line */ private String[] arguments; /** * Spring application context */ private ConfigurableApplicationContext context; /** * singleton instance of the application container */ private static ApplicationContainer instance; /** * collection of tasks to run upon application shutdown */ private static final Set<ComparableTask> shutdownTasks = new TreeSet<ComparableTask>(); /** * optional command-line parameter that indicates a different context file to use */ public static final String CONTEXT_FILE_PROP = "org.marketcetera.contextFile"; //$NON-NLS-1$ /** * indicates the name of the context file to use */ private static String contextFilename = "application.xml"; /** * exit code to return on exit */ protected static int exitCode = 0; }