package edu.sc.seis.sod; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Properties; import java.util.Timer; import java.util.TimerTask; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.log4j.BasicConfigurator; import org.apache.log4j.PropertyConfigurator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import edu.iris.Fissures.IfNetwork.NetworkNotFound; import edu.iris.Fissures.model.MicroSecondDate; import edu.iris.Fissures.model.TimeInterval; import edu.sc.seis.fissuresUtil.Dissendium; import edu.sc.seis.fissuresUtil.cache.RetryStrategy; import edu.sc.seis.fissuresUtil.chooser.ClockUtil; import edu.sc.seis.fissuresUtil.database.ConnMgr; import edu.sc.seis.fissuresUtil.exceptionHandler.Extractor; import edu.sc.seis.fissuresUtil.exceptionHandler.GlobalExceptionHandler; import edu.sc.seis.fissuresUtil.exceptionHandler.MailExceptionReporter; import edu.sc.seis.fissuresUtil.exceptionHandler.MissingPropertyException; import edu.sc.seis.fissuresUtil.exceptionHandler.ResultMailer; import edu.sc.seis.fissuresUtil.exceptionHandler.SystemOutReporter; import edu.sc.seis.fissuresUtil.exceptionHandler.WindowConnectionInterceptor; import edu.sc.seis.fissuresUtil.hibernate.AbstractHibernateDB; import edu.sc.seis.fissuresUtil.hibernate.HibernateUtil; import edu.sc.seis.seisFile.fdsnws.FDSNWSException; import edu.sc.seis.sod.hibernate.SodDB; import edu.sc.seis.sod.hibernate.StatefulEvent; import edu.sc.seis.sod.hibernate.StatefulEventDB; import edu.sc.seis.sod.status.IndexTemplate; import edu.sc.seis.sod.status.OutputScheduler; import edu.sc.seis.sod.status.TemplateFileLoader; import edu.sc.seis.sod.validator.Validator; public class Start { static { GlobalExceptionHandler.add(new Extractor() { public boolean canExtract(Throwable throwable) { return (throwable instanceof FDSNWSException); } public String extract(Throwable throwable) { String out = ""; if(throwable instanceof FDSNWSException) { FDSNWSException mie = (FDSNWSException)throwable; out += "URI: " + mie.getTargetURI() + "\n"; } return out; } public Throwable getSubThrowable(Throwable throwable) { return null; } }); GlobalExceptionHandler.add(new Extractor() { public boolean canExtract(Throwable throwable) { return (throwable instanceof org.apache.velocity.exception.MethodInvocationException); } public String extract(Throwable throwable) { String out = ""; if(throwable instanceof org.apache.velocity.exception.MethodInvocationException) { org.apache.velocity.exception.MethodInvocationException mie = (org.apache.velocity.exception.MethodInvocationException)throwable; out += "Method Name: " + mie.getMethodName() + "\n"; out += "reference Name: " + mie.getReferenceName() + "\n"; } return out; } public Throwable getSubThrowable(Throwable throwable) { if(throwable instanceof org.apache.velocity.exception.MethodInvocationException) { return ((org.apache.velocity.exception.MethodInvocationException)throwable).getWrappedThrowable(); } return null; } }); GlobalExceptionHandler.add(new Extractor() { public boolean canExtract(Throwable throwable) { return (throwable instanceof org.hibernate.JDBCException); } public String extract(Throwable throwable) { String out = ""; if(throwable instanceof org.hibernate.JDBCException) { org.hibernate.JDBCException mie = (org.hibernate.JDBCException)throwable; out += mie.getMessage(); out += "\nSQL: "+mie.getSQL(); out += "\nSQLState: "+mie.getSQLState(); if (AbstractHibernateDB.isSessionOpen()) { out += "\n\nSession:\n"+AbstractHibernateDB.getSession().toString(); } else { out+="\nSession: none\n"; } } return out; } public Throwable getSubThrowable(Throwable throwable) { if(throwable instanceof org.hibernate.JDBCException) { Throwable sub = ((org.hibernate.JDBCException)throwable).getSQLException(); if (sub != throwable) { return sub; } } return null; } }); GlobalExceptionHandler.add(new Extractor() { public boolean canExtract(Throwable throwable) { return (throwable instanceof FDSNWSException); } public String extract(Throwable throwable) { String out = ""; if(throwable instanceof FDSNWSException) { FDSNWSException mie = (FDSNWSException)throwable; out += mie.getMessage(); out += "\nURI: "+mie.getTargetURI(); } return out; } public Throwable getSubThrowable(Throwable throwable) { return null; } }); GlobalExceptionHandler.registerWithAWTThread(); } /** * Creates a new <code>Start</code> instance set to use the XML config * file in confFilename */ public Start(Args args) throws Exception { this(args, new InputSourceCreator(), null, false); } public static class InputSourceCreator { public InputSource create() throws IOException { return createInputSource(Start.class.getClassLoader(), configFileName); } } public Start(Args args, InputSourceCreator sourceMaker, Properties props, boolean commandLineToolRun) throws Exception { Start.args = args; this.creator = sourceMaker; this.commandLineToolRun = commandLineToolRun; configFileName = args.getRecipe(); if(props == null) { loadProps(); } else { // this is for command line tools and unit tests Start.props = props; } try { setConfig(createDoc(sourceMaker.create(), configFileName).getDocumentElement()); } catch(IOException io) { informUserOfBadFileAndExit(configFileName); } catch(Exception e) { GlobalExceptionHandler.handle("Trouble creating xml document", e); } logger.info("logging configured"); logger.info("SOD version "+Version.current().getVersion()); logger.info("Args: "+args.toString()); logger.info("Recipe: "+configFileName); if(!commandLineToolRun) { validate(sourceMaker); } if (args.isPrintRecipe()) { BufferedReader in = new BufferedReader(new InputStreamReader(createInputStream(configFileName))); String line; while((line = in.readLine()) != null) { System.out.println(line); } System.out.flush(); System.exit(0); } Dissendium.insertOrbProp(Start.props); if (args.isQuickAndDirty()) { Start.props.put("fissuresUtil.database.url", "jdbc:hsqldb:mem:SodDB"); logger.info("Database: memory"); } else { logger.info("Database: "+Start.props.get("fissuresUtil.database.url")); if (ConnMgr.getURL().startsWith(HSQL_FILE_URL)) { File dbFile = new File(ConnMgr.getURL().substring(HSQL_FILE_URL.length())+".log"); if (dbFile.exists()) { logger.info("Database file exists: "+dbFile.getPath()); } else { logger.info("Database file does not exist, clean start."); } } } parseArms(config.getChildNodes()); } private void validate(InputSourceCreator sourceMaker) { try { logger.info("validating recipe..."); Validator validator = new Validator(Validator.SOD_SCHEMA_LOC); if(!validator.validate(sourceMaker.create())) { logger.info("Invalid recipe file!"); allHopeAbandon(validator.getErrorMessage()); } else { logger.info("Congratulations, valid recipe."); if (! args.isPrintRecipe()) { System.out.println("Congratulations, valid recipe."); } } if(args.onlyValidate()) { System.exit(0); } } catch(Exception e) { GlobalExceptionHandler.handle("Problem configuring schema validator", e); exit("Problem configuring schema validator: " + e.getMessage()); } } private static void informUserOfBadFileAndExit(String confFilename) { File configFile = new File(confFilename); System.err.println("You told SOD to use " + configFile.getAbsolutePath() + " as its recipe file"); if(configFile.exists()) { System.err.println("SOD was unable to open it. Make sure the file is readable."); } else { System.err.println("SOD could find no such file. Make sure the file exists"); } System.exit(0); } public static void informUserOfBadNetworkAndExit(String networkCode, NetworkNotFound nnf) { logger.error("Can't find "+networkCode+" network from server", nnf); String msg = "You told SOD to use the '" + networkCode + "' network, but the server does not think it exists."; informUserOfBadQueryAndExit(msg, nnf); } public static void informUserOfBadQueryAndExit(String message, Exception e) { logger.error(message, e); System.err.println(); System.err.println(message); System.err.println(); System.err.println(" SOD is now cowardly quitting."); armFailure = true; wakeUpAllArms(); } public static MicroSecondDate getStartTime() { return startTime; } public static TimeInterval getElapsedTime() { return ClockUtil.now().subtract(startTime); } public static String getConfigFileName() { return configFileName; } public void setupDatabaseForUnitTests() throws ConfigurationException { initDatabase(); } protected void initDatabase() throws ConfigurationException { ConnMgr.installDbProperties(props, args.getInitialArgs()); warnIfDatabaseExists(); synchronized(HibernateUtil.class) { HibernateUtil.setUpFromConnMgr(props, getClass().getResource("/edu/sc/seis/sod/data/ehcache.xml")); SodDB.configHibernate(HibernateUtil.getConfiguration()); Iterator it = getRunProps().getHibernateConfig().iterator(); while(it.hasNext()) { String res = (String)it.next(); logger.debug("Adding resource to HibernateUtil: "+res); HibernateUtil.getConfiguration().addResource(res); } } try { AbstractHibernateDB.deploySchema(); } catch (Exception e) { throw new ConfigurationException("Unable to set up database", e); } // check that hibernate is ok SodDB sodDb = SodDB.getSingleton(); sodDb.commit(); } protected String HSQL_FILE_URL = "jdbc:hsqldb:file:"; protected void warnIfDatabaseExists() { if (getRunProps().warnIfDatabaseExists()) { // only matters if hsql??? if (ConnMgr.getURL().startsWith(HSQL_FILE_URL)) { File dbFile = new File(ConnMgr.getURL().substring(HSQL_FILE_URL.length())+".log"); if (dbFile.exists()) { allHopeAbandon("The database for this run, "+dbFile +" appears to already exist. This is fine if you want to restart a run that crashed," +" but if you are trying to start a fresh SOD run, you may wish to delete this database directory first." +" Otherwise, SOD will consider any work in this database as already completed and will not redo it."); } } } } public static void loadProps(InputStream propStream, Properties baseProps) throws IOException { baseProps.load(propStream); propStream.close(); } private void loadProps() throws IOException { // get some defaults loadProps((Start.class).getClassLoader() .getResourceAsStream(DEFAULT_PROPS), props); if(args.hasProps()) { try { loadProps(args.getProps(), props); } catch(IOException io) { System.err.println("Unable to load props file: " + io.getMessage()); System.err.println("Quitting until the error is corrected"); System.exit(1); } } if (args.isDebug()) { String rootCat = props.getProperty("log4j.rootCategory"); if (rootCat != null && rootCat.length()>0 && rootCat.contains(",")) { String head = rootCat.substring(0, rootCat.indexOf(',')); String tail = rootCat.substring(rootCat.indexOf(',')); props.setProperty("log4j.rootCategory", "debug"+tail); logger.info("--debug so resetting log4j.rootCategory from '"+rootCat+"' to '"+props.getProperty("log4j.rootCategory")+"'"); } else { // oh well, just guess props.setProperty("log4j.rootCategory", "debug, R, C, E"); } } PropertyConfigurator.configure(props); // Error html dir and output should be set up now, so remove the // Std out reporter GlobalExceptionHandler.remove(sysOutReporter); } public static InputSource createInputSource(ClassLoader cl, String loc) throws IOException { return new InputSource(new InputStreamReader(createInputStream(cl, loc))); } public static InputStream createInputStream(String loc) throws IOException, MalformedURLException, FileNotFoundException { return createInputStream(Start.class.getClassLoader(), loc); } public static InputStream createInputStream(ClassLoader cl, String loc) throws IOException, MalformedURLException, FileNotFoundException { InputStream in = null; if(loc.startsWith("http:") || loc.startsWith("ftp:")) { in = new URL(loc).openConnection().getInputStream(); } else if(loc.startsWith("jar:")) { URL url = TemplateFileLoader.getUrl(cl, loc); in = url.openConnection().getInputStream(); } else { in = new FileInputStream(loc); } if(in == null) { throw new IOException("Unable to load configuration file " + loc); } return new BufferedInputStream(in); } public static RetryStrategy createRetryStrategy(int numRetries) { if(commandName.equals("sod")) { return new UserReportRetryStrategy(numRetries, "SOD will pick up where it left off when restarted."); } else { return new UserReportRetryStrategy(numRetries); } } public static void setCommandName(String name) { commandName = name; } public static void setConfig(Element config) { Start.config = config; } public static AbstractWaveformRecipe getWaveformRecipe() { return waveformRecipe; } public static EventArm getEventArm() { return event; } public static NetworkArm getNetworkArm() { return network; } public static WaveformArm[] getWaveformArmArray() { return waveforms; } public static RunProperties getRunProps() { if(runProps == null) { try { runProps = new RunProperties(); } catch(ConfigurationException e) { throw new RuntimeException(e); } } return runProps; } public static Document createDoc(InputSource source, String filename) throws SAXException, IOException, ParserConfigurationException { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); DocumentBuilder builder = factory.newDocumentBuilder(); builder.setErrorHandler(new SimpleErrorHandler(filename)); Document doc = builder.parse(source); return doc; } public static ResultMailer getResultMailer() throws ConfigurationException { if(mailer != null) { return mailer; } throw new ConfigurationException("no mailer configured"); } public static void addResultMailer(Properties mailProps) throws MissingPropertyException { if(mailer == null && mailProps.containsKey("mail.smtp.host")) { mailer = new ResultMailer(mailProps); } } private static ResultMailer mailer; public void start() throws Exception { // startTime = ClockUtil.now(); startTime = new MicroSecondDate(); if(runProps.removeDatabase() || getArgs().isClean()) { cleanHSQLDatabase(); } initDatabase(); CommonAccess.initialize(props, args.getInitialArgs()); if(!commandLineToolRun) { new UpdateChecker(false); handleStartupRunProperties(); checkDBVersion(); checkConfig(creator.create()); } // this next line sets up the status page for exception reporting, // so // it should be as early as possible in the startup sequence IndexTemplate indexTemplate = null; if(runProps.doStatusPages()) { indexTemplate = new IndexTemplate(); } startArms(); if(runProps.doStatusPages()) { indexTemplate.performRegistration(); } if(!commandLineToolRun) { MailExceptionReporter.addMailExceptionReporter(props);; addResultMailer(props); if(runProps.checkpointPeriodically()) { new PeriodicCheckpointer(); } if(runProps.loserEventCleaner()) { TotalLoserEventCleaner loserCleaner = new TotalLoserEventCleaner(getRunProps().getEventLag()); Timer t = new Timer("TotalLoserCleaner", true); t.schedule(loserCleaner, 0, 7*24*60*60*1000); } } } public void allHopeAbandon(String message) { logger.info("All hope abandon: " + message); System.err.println(); System.err.println("******************************************************************"); System.err.println(); System.err.println(message); System.err.println(); System.err.println(" All hope abandon, ye who enter in!"); System.err.println(); System.err.println("******************************************************************"); if(args.waitOnError()) { System.err.println(); for(int i = 10; i >= 0; i--) { try { Thread.sleep(1000); System.err.print(" " + i); } catch(InterruptedException e) {} } } System.err.println(); System.err.println(); System.err.println("And lo! towards us coming in a boat"); System.err.println(" An old man, hoary with the hair of eld,"); System.err.println(" Crying: \"Woe unto you, ye souls depraved!"); System.err.println(""); System.err.println("Hope nevermore to look upon the heavens;"); System.err.println(" I come to lead you to the other shore,"); System.err.println(" To the eternal shades in heat and frost.\""); System.err.println(); System.err.println(); System.err.println(" ...a brave soul trudges on."); } static void parseArms(NodeList armNodes) throws Exception { runProps = new RunProperties(); waveforms = new WaveformArm[0]; // just in case a non-waveform run for (int i = 0; i < armNodes.getLength(); i++) { if (armNodes.item(i) instanceof Element) { Element el = (Element)armNodes.item(i); if (el.getTagName().equals("properties")) { // load the properties from the configurationfile. runProps.addProperties(el); } else if (el.getTagName().equals("eventArm") && args.doEventArm()) { event = new EventArm(el); } else if (el.getTagName().equals("networkArm") && args.doNetArm()) { network = new NetworkArm(el); } else if (el.getTagName().startsWith("waveform") && args.doWaveformArm()) { if (el.getTagName().equals("waveformVectorArm")) { SodDB.setDefaultEcpClass(EventVectorPair.class); waveformRecipe = new MotionVectorArm(el); } else if (el.getTagName().equals("waveformArm")) { SodDB.setDefaultEcpClass(EventChannelPair.class); waveformRecipe = new LocalSeismogramArm(el); } else { throw new ConfigurationException("unknown waveform arm type: " + el.getTagName()); } } } } } private void startArms() throws Exception { if (waveformRecipe != null) { if (runProps.reopenSuspended()) { Runnable reopenEvents = new Runnable() { public void run() { // check for ECPairs that need to be reprocessed SodDB.getSingleton().reopenSuspendedEventChannelPairs(Start.getRunProps() .getEventChannelPairProcessing(), (waveformRecipe instanceof LocalSeismogramArm)); SodDB.commit(); } }; Thread t = new Thread(reopenEvents, "Reopen Suspended ECPS"); t.start(); } // check for events that are "in progress" due to halt or reset StatefulEventDB eventDb = StatefulEventDB.getSingleton(); for (StatefulEvent ev = eventDb.getNext(Standing.IN_PROG); ev != null; ev = eventDb.getNext(Standing.IN_PROG)) { WaveformArm.createEventNetworkPairs(ev); } eventDb.commit(); int poolSize = runProps.getNumWaveformWorkerThreads(); waveforms = new WaveformArm[poolSize]; for (int j = 0; j < waveforms.length; j++) { waveforms[j] = new WaveformArm(j, waveformRecipe); } Timer retryTimer = new Timer("retry loader", true); retryTimer.schedule(new TimerTask() { public void run() { // only run if the retry queue is empty if (!SodDB.getSingleton().isESPTodo()) { SodDB.getSingleton().populateRetryToDo(); // db connection recycling SodDB.rollback(); } } }, 0, 10 * 60 * 1000); } if (waveformRecipe == null && event != null) { event.setWaitForWaveformProcessing(false); } if (RUN_ARMS) { // Make sure the OutputScheduler exists when the arms are started OutputScheduler.getDefault(); startArm(network, "NetworkArm"); startArm(event, "EventArm"); for (int i = 0; i < waveforms.length; i++) { startArm(waveforms[i], waveforms[i].getName()); } } for (Iterator iter = armListeners.iterator(); iter.hasNext();) { ((ArmListener)iter.next()).started(); } } public static void add(ArmListener listener) { armListeners.add(listener); } private void startArm(Arm arm, String name) throws ConfigurationException { if(arm != null) { for(Iterator iter = armListeners.iterator(); iter.hasNext();) { ArmListener element = (ArmListener)iter.next(); element.starting(arm); } new Thread(arm, arm.getName()).start(); logger.debug(name + " started"); } else { logger.debug(name + " doesn't exist"); } } void cleanHSQLDatabase() { String dbUrl = ConnMgr.getURL(); if(!dbUrl.startsWith("jdbc:hsqldb") || dbUrl.indexOf("hsql://") != -1 || !(dbUrl.indexOf(DATABASE_DIR) != -1)) { logger.warn("The database isn't the default local hsqldb, so it couldn't be deleted as specified by the properties"); return; } File dbDir = new File(DATABASE_DIR); if(dbDir.exists()) { logger.info("Removing old database"); File[] dbFiles = dbDir.listFiles(); for(int i = 0; i < dbFiles.length; i++) { if(!dbFiles[i].delete()) { logger.warn("Unable to delete " + dbFiles[i] + " when removing the previous database. The old database might still exist"); } } if(!dbDir.delete()) { logger.warn("Unable to delete the database directory."); } } } private void handleStartupRunProperties() { if(runProps.reopenEvents()) { StatefulEventDB eventDb = StatefulEventDB.getSingleton(); eventDb.restartCompletedEvents(); } } private void checkDBVersion() { SodDB sodDb = SodDB.getSingleton(); try { logger.debug("SodDB in check DBVersion:" + sodDb); Version dbVersion = sodDb.getDBVersion(); SodDB.commit(); if(dbVersion == null) { throw new RuntimeException("db version is null"); } if(Version.hasSchemaChangedSince(dbVersion.getVersion())) { System.err.println("SOD version: " + Version.current().getVersion()); System.err.println("Database version: " + dbVersion.getVersion()); System.err.println("Your database was created with an older version " + "of SOD."); allHopeAbandon("There has been a change in the database " + "structure since the database was created! " + "Continuing this sod run is not advisable!"); } } catch(Exception e) { logger.error("exception", e); SodDB.rollback(); GlobalExceptionHandler.handle("Trouble checking database version", e); } } private void checkConfig(InputSource is) { SodDB sodDb = SodDB.getSingleton(); try { Thread.sleep(1000); } catch(InterruptedException e1) {} try { SodConfig conf = new SodConfig(new BufferedReader(is.getCharacterStream())); SodConfig dbConfig = sodDb.getCurrentConfig(); if(dbConfig == null) { sodDb.putConfig(conf); } else if(dbConfig.getConfig().equals(conf.getConfig())) {} else { if(args.replaceDBConfig()) { sodDb.putConfig(conf); } else { allHopeAbandon("Your config file has changed since your last run. " + "It may not be advisable to continue this SOD run."); } } SodDB.commit(); } catch(Exception e) { GlobalExceptionHandler.handle("Trouble checking stored config file", e); SodDB.rollback(); } } private boolean commandLineToolRun; private InputSourceCreator creator; private static Args args; public static Args getArgs() { return args; } public static Element getConfig() { return config; } // this is not the real exception reporter, but do this to catch // initialization exceptions so they are not lost in the log file private static SystemOutReporter sysOutReporter = new SystemOutReporter(); public static void checkGCJ() { if (System.getProperty("java.vm.name").equals("GNU libgcj")) { System.err .println("You are running GNU's version of Java, gcj, which doesn't have all the features SOD requires. Instead, use Sun's Java from http://java.sun.com."); System.exit(-1); } } public static void main(String[] args) { try { checkGCJ(); GlobalExceptionHandler.add(new WindowConnectionInterceptor()); GlobalExceptionHandler.add(sysOutReporter); // start up log4j before read props so at least there is some // logging // later we will use PropertyConfigurator to really configure log4j BasicConfigurator.configure(); Start start = new Start(new Args(args)); logger.info("Start start()"); start.start(); } catch(UserConfigurationException e) { logger.error("User configuration problem, quiting", e); exit(e.getMessage() + " SOD will quit now and continue to cowardly quit until this is corrected."); } catch(Throwable e) { GlobalExceptionHandler.handle("Problem in main, quiting", e); exit("Quitting due to error: " + e.getMessage()); } logger.info("Finished starting all threads."); } // end of main () public static void exit(String reason) { System.err.println(reason); System.exit(1); } public static void add(Properties newProps) { props.putAll(newProps); } public static void cataclysmicFailureOfUnbelievableProportions() { try { System.err.println("Oh boy, this is really bad. No, it is even worse then that."); logger.error("horror of horrors..."); } catch(Throwable t) { } System.exit(1); } public static void simpleArmFailure(Arm arm, String reason) { armFailure = true; logger.error("Arm " + arm.getName() + " failed: "+reason+" Sod is giving up and quiting."); logger.debug("Arm " + arm.getName() + " failure stack trace: "+reason, new Exception(reason)); wakeUpAllArms(); } public static void armFailure(Arm arm, Throwable t) { armFailure = true; GlobalExceptionHandler.handle("Problem running " + arm.getName() + ", SOD is exiting abnormally. " + "Please email this to the sod development team at sod@seis.sc.edu", t); logger.error("Arm " + arm.getName() + " failed. Sod is giving up and quiting", t); wakeUpAllArms(); } public static void wakeUpAllArms() { // wake up any sleeping arms Arm[] arms = new Arm[] {network, event}; for(int i = 0; i < arms.length; i++) { if (arms[i] != null) { synchronized(arms[i]) { arms[i].notifyAll(); } } } for (int i = 0; i < waveforms.length; i++) { if (waveforms[i] != null) { synchronized(waveforms[i]) { waveforms[i].notifyAll(); } } } synchronized(OutputScheduler.getDefault()) { OutputScheduler.getDefault().notify(); } } public static boolean isArmFailure() { return armFailure; } private static boolean armFailure = false; public static final String DEFAULT_PARSER = "org.apache.xerces.parsers.SAXParser"; private static Element config; private static Logger logger = LoggerFactory.getLogger(Start.class); private static WaveformArm[] waveforms; private static AbstractWaveformRecipe waveformRecipe; private static Properties props = System.getProperties(); private static RunProperties runProps; private static EventArm event; protected static NetworkArm network; private static String configFileName; private static String commandName = "sod"; private static MicroSecondDate startTime; private static String DATABASE_DIR = "SodDb"; public static final String DBURL_KEY = "fissuresUtil.database.url"; public static boolean RUN_ARMS = true; private static List armListeners = new ArrayList(); public static final String TUTORIAL_LOC = "jar:edu/sc/seis/sod/data/configFiles/demo.xml"; public static final String DEFAULT_PROPS = "edu/sc/seis/sod/data/sod.prop"; }// Start