Bootstrap.java

  1. /*
  2.  * Bootstrap
  3.  */
  4. package gov.usgs.earthquake.distribution;

  5. import gov.usgs.util.Config;
  6. import gov.usgs.util.Configurable;
  7. import gov.usgs.util.StreamUtils;
  8. import gov.usgs.util.logging.LoggingOutputStream;
  9. import gov.usgs.util.logging.SimpleLogFileHandler;
  10. import gov.usgs.util.logging.SimpleLogFormatter;
  11. import gov.usgs.util.logging.StdOutErrLevel;

  12. import java.io.File;
  13. import java.io.IOException;
  14. import java.io.InputStream;
  15. import java.io.PrintStream;
  16. import java.text.SimpleDateFormat;
  17. import java.util.ArrayList;
  18. import java.util.logging.ConsoleHandler;
  19. import java.util.logging.Formatter;
  20. import java.util.logging.Handler;
  21. import java.util.logging.Level;
  22. import java.util.logging.LogManager;
  23. import java.util.logging.Logger;
  24. import java.util.logging.SimpleFormatter;
  25. import java.util.logging.XMLFormatter;

  26. /**
  27.  * Bootstrap is a class used to start an application.
  28.  *
  29.  * It loads a configuration file, sets up initial logging, and starts a
  30.  * configurable main method.
  31.  *
  32.  * @author jmfee
  33.  *
  34.  */
  35. public class Bootstrap {

  36.     // public static
  37.     static {
  38.         gov.usgs.util.protocolhandlers.data.Handler.register();
  39.     }

  40.     /** Default JAR config path. */
  41.     public static final String JAR_CONFIGFILE = "etc/config/config.ini";

  42.     /** Argument for config file. */
  43.     public static final String CONFIGFILE_ARGUMENT = "--configFile=";
  44.     /** Default config file. */
  45.     public static final String DEFAULT_CONFIGFILE = "config.ini";

  46.     /** Whether to test config only. */
  47.     public static final String CONFIG_TEST_ARGUMENT = "--configTest";

  48.     /** Property for log format. */
  49.     public static final String LOGFORMAT_PROPERTY_NAME = "logformat";
  50.     /** log format value for "pdl" format */
  51.     public static final String LOGFORMAT_PDL = "pdl";
  52.     /** log format value for java "simple" format */
  53.     public static final String LOGFORMAT_SIMPLE = "simple";
  54.     /** log format value for java "xml" format */
  55.     public static final String LOGFORMAT_XML = "xml";
  56.     /** Default log format is "simple". */
  57.     public static final String DEFAULT_LOGFORMAT = LOGFORMAT_PDL;

  58.     /** Property for log level. */
  59.     public static final String LOGLEVEL_PROPERTY_NAME = "loglevel";
  60.     /** Default log level is "INFO". */
  61.     public static final String DEFAULT_LOGLEVEL = "INFO";

  62.     /** Property for log directory. */
  63.     public static final String LOGDIRECTORY_PROPERTY_NAME = "logdirectory";
  64.     /** Default log directory is "log". */
  65.     public static final String DEFAULT_LOGDIRECTORY = "log";

  66.     /** Property for log file pattern. */
  67.     public static final String LOGFILE_PROPERTY_NAME = "logfile";
  68.     /** Default log file pattern is "yyyyMMdd'.log'". */
  69.     public static final String DEFAULT_LOGFILE = "yyyyMMdd'.log'";

  70.     /** Property for console redirect. */
  71.     public static final String CONSOLEREDIRECT_PROPERTY_NAME = "redirectconsole";
  72.     /** Default console redirect value is "false" (don't redirect). */
  73.     public static final String DEFAULT_CONSOLEREDIRECT = "false";

  74.     /** Property used to disable tracker updates. */
  75.     public static final String ENABLE_TRACKER_PROPERTY_NAME = "enableTracker";

  76.     /** Argument for mainclass. */
  77.     public static final String MAINCLASS_ARGUMENT = "--mainclass=";
  78.     /** Property for mainclass. */
  79.     public static final String MAINCLASS_PROPERTY_NAME = "mainclass";
  80.     /** Default mainclass is "gov.usgs.earthquake.distribution.ProductClient. */
  81.     public static final String DEFAULT_MAINCLASS = "gov.usgs.earthquake.distribution.ProductClient";
  82.     /** Argument for version */
  83.     public static final String VERSION_ARGUMENT = "--version";

  84.     // private static

  85.     /** Private logging object. */
  86.     private static final Logger LOGGER = Logger.getLogger(Bootstrap.class
  87.             .getName());


  88.     /** List of logger objects that have level overrides configured. */
  89.     private final ArrayList<Logger> loggers = new ArrayList<Logger>();

  90.     /** Constructor */
  91.     public Bootstrap() {
  92.     }

  93.     // members

  94.     /**
  95.      * Read configuration from inside jar file, and configFile.
  96.      *
  97.      * @param configFile
  98.      *            config file to load.
  99.      * @return
  100.      *            config
  101.      * @throws IOException
  102.      *            if IO error occurs
  103.      */
  104.     public Config loadConfig(final File configFile) throws IOException {
  105.         Config config = new Config();

  106.         // load defaults from jar file
  107.         InputStream in = Bootstrap.class.getClassLoader().getResourceAsStream(
  108.                 JAR_CONFIGFILE);
  109.         if (in != null) {
  110.             try {
  111.                 config.load(in);
  112.             } finally {
  113.                 StreamUtils.closeStream(in);
  114.             }
  115.         } else {
  116.             LOGGER.config("Jar configuration not found");
  117.         }

  118.         // override settings with a config file
  119.         if (configFile.exists()) {
  120.             LOGGER.config("Loading configuration file "
  121.                     + configFile.getCanonicalPath());

  122.             config = new Config(config);
  123.             in = StreamUtils.getInputStream(configFile);
  124.             try {
  125.                 config.load(in);
  126.             } finally {
  127.                 StreamUtils.closeStream(in);
  128.             }
  129.         }

  130.         return config;
  131.     }

  132.     /**
  133.      * Sets up LogManager
  134.      * @param config Config file
  135.      */
  136.     public void setupLogging(final Config config) {
  137.         final LogManager logManager = LogManager.getLogManager();
  138.         logManager.reset();
  139.         loggers.clear();

  140.         // logging is noisy without this
  141.         for (final String name : new String[]{
  142.             "com.sun.activation",
  143.             "com.sun.xml.bind",
  144.             "javax.xml.bind",
  145.             "org.glassfish.grizzly",
  146.             "org.glassfish.tyrus",
  147.             "sun.awt.X11.timeoutTask.XToolkit"
  148.         }) {
  149.             final Logger logger = Logger.getLogger(name);
  150.             logger.setLevel(Level.INFO);
  151.             // save reference to logger since LogManager uses weakref
  152.             loggers.add(logger);
  153.         };

  154.         final Level level = Level.parse(config.getProperty(LOGLEVEL_PROPERTY_NAME,
  155.                 DEFAULT_LOGLEVEL));
  156.         final String logDirectory = config.getProperty(LOGDIRECTORY_PROPERTY_NAME,
  157.                 DEFAULT_LOGDIRECTORY);
  158.         LOGGER.config("Logging Level '" + level + "'");
  159.         LOGGER.config("Log directory '" + logDirectory + "'");

  160.         Logger rootLogger = Logger.getLogger("");
  161.         rootLogger.setLevel(level);

  162.         try {
  163.             File logDirectoryFile = new File(logDirectory);
  164.             if (!logDirectoryFile.exists()) {
  165.                 LOGGER.fine("Creating log directory");
  166.                 if (!logDirectoryFile.mkdirs()) {
  167.                     LOGGER.warning("Unable to create log directory");
  168.                 }
  169.             }

  170.             // filepattern, maxBytesPerFile, maxFiles, append
  171.             // FileHandler handler = new FileHandler(logFile, 100000, 10, true);
  172.             Handler handler = new SimpleLogFileHandler(logDirectoryFile,
  173.                     new SimpleDateFormat(DEFAULT_LOGFILE));
  174.             handler.setLevel(level);
  175.             rootLogger.addHandler(handler);
  176.         } catch (Exception e) {
  177.             LOGGER.log(Level.WARNING, "Unable to create log file handler", e);
  178.         }

  179.         String redirectConsole = config.getProperty(
  180.                 CONSOLEREDIRECT_PROPERTY_NAME, DEFAULT_CONSOLEREDIRECT);
  181.         if (!redirectConsole.equals(DEFAULT_CONSOLEREDIRECT)) {
  182.             // default is off, so enable
  183.             System.err.println("Redirecting STDOUT and STDERR to log file");
  184.             System.setOut(new PrintStream(new LoggingOutputStream(Logger
  185.                     .getLogger("stdout"), StdOutErrLevel.STDOUT)));
  186.             System.setErr(new PrintStream(new LoggingOutputStream(Logger
  187.                     .getLogger("stderr"), StdOutErrLevel.STDERR)));
  188.         } else {
  189.             ConsoleHandler handler = new ConsoleHandler();
  190.             handler.setLevel(level);
  191.             rootLogger.addHandler(handler);
  192.         }

  193.         Formatter formatter;
  194.         String logFormat = config.getProperty(
  195.                 LOGFORMAT_PROPERTY_NAME, DEFAULT_LOGFORMAT);
  196.         if (logFormat.equals(LOGFORMAT_SIMPLE)) {
  197.             // built in simple formatter
  198.             formatter = new SimpleFormatter();
  199.         } else if (logFormat.equals(LOGFORMAT_XML)) {
  200.             // built in xml formatter
  201.             formatter = new XMLFormatter();
  202.         } else {
  203.             // pdl style simple formatter
  204.             formatter = new SimpleLogFormatter();
  205.         }
  206.         for (Handler handler : rootLogger.getHandlers()) {
  207.             handler.setFormatter(formatter);
  208.         }
  209.     }

  210.     public static void main(final String[] args) throws Exception {
  211.         StringBuffer argumentList = new StringBuffer();
  212.         boolean configTest = false;

  213.         String className = null;

  214.         // use default config file
  215.         File configFile = new File(DEFAULT_CONFIGFILE);
  216.         for (String arg : args) {
  217.             argumentList.append(arg).append(" ");
  218.             if (arg.startsWith(CONFIGFILE_ARGUMENT)) {
  219.                 // unless config file argument provided
  220.                 configFile = new File(arg.replace(CONFIGFILE_ARGUMENT, ""));
  221.             } else if (arg.equals(CONFIG_TEST_ARGUMENT)) {
  222.                 configTest = true;
  223.             } else if (arg.startsWith(MAINCLASS_ARGUMENT)) {
  224.                 className = arg.replace(MAINCLASS_ARGUMENT, "");
  225.             } else if (arg.equals(VERSION_ARGUMENT)) {
  226.                 System.err.println("Product Distribution Client");
  227.                 System.err.println(ProductClient.RELEASE_VERSION);
  228.                 System.exit(0);
  229.             }
  230.         }

  231.         Bootstrap bootstrap = new Bootstrap();

  232.         // load configuration file
  233.         Config config = bootstrap.loadConfig(configFile);

  234.         // set global config object
  235.         Config.setConfig(config);

  236.         // setup logging based on configuration
  237.         bootstrap.setupLogging(config);

  238.         // java and os information
  239.         LOGGER.config("java.vendor = " + System.getProperty("java.vendor"));
  240.         LOGGER.config("java.version = " + System.getProperty("java.version"));
  241.         LOGGER.config("java.home = " + System.getProperty("java.home"));
  242.         LOGGER.config("os.arch = " + System.getProperty("os.arch"));
  243.         LOGGER.config("os.name = " + System.getProperty("os.name"));
  244.         LOGGER.config("os.version = " + System.getProperty("os.version"));
  245.         LOGGER.config("user.dir = " + System.getProperty("user.dir"));
  246.         LOGGER.config("user.name = " + System.getProperty("user.name"));

  247.         // log command line arguments
  248.         LOGGER.fine("Command line arguments: " + argumentList.toString().trim());

  249.         // configure whether tracker updates are sent.
  250.         String enableTrackerProperty = config
  251.                 .getProperty(ENABLE_TRACKER_PROPERTY_NAME);
  252.         if (Boolean.valueOf(enableTrackerProperty)) {
  253.             LOGGER.warning("Enabled tracker updates,"
  254.                     + " this is usually not a good idea.");
  255.             ProductTracker.setTrackerEnabled(true);
  256.         }

  257.         // lookup main class
  258.         if (className == null) {
  259.             // no argument specified, check configuration
  260.             className = config.getProperty(MAINCLASS_PROPERTY_NAME,
  261.                     DEFAULT_MAINCLASS);
  262.         }

  263.         // invoke main class main(String[] args) method
  264.         LOGGER.config("Loading main class " + className);
  265.         Bootstrappable main = null;
  266.         try {
  267.             main = (Bootstrappable) Class.forName(className)
  268.                     .getConstructor().newInstance();
  269.         } catch (ClassCastException cce) {
  270.             LOGGER.log(Level.SEVERE,
  271.                     "Main class must implement the Bootstrappable interface",
  272.                     cce);
  273.             System.exit(1);
  274.         }

  275.         // use the configurable interface when available
  276.         if (main instanceof Configurable) {
  277.             Configurable configurable = ((Configurable) main);
  278.             configurable.setName("main");
  279.             try {
  280.                 configurable.configure(config);
  281.             } catch (Exception e) {
  282.                 LOGGER.log(Level.SEVERE, "Exception loading configuration ", e);
  283.                 System.exit(1);
  284.             }
  285.         }

  286.         // configuration loaded okay
  287.         LOGGER.config("Configuration loaded");
  288.         if (configTest) {
  289.             // exit successfully
  290.             System.exit(0);
  291.         }

  292.         // run main instance
  293.         LOGGER.config("Bootstrap complete, running main class\n");
  294.         try {
  295.             main.run(args);
  296.         } catch (Exception e) {
  297.             LOGGER.log(Level.SEVERE, "Main class threw exception, exiting", e);
  298.             System.exit(Bootstrappable.RUN_EXCEPTION_EXIT_CODE);
  299.         }
  300.     }

  301. }