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.logging.LoggingOutputStream;
  8. import gov.usgs.util.logging.SimpleLogFileHandler;
  9. import gov.usgs.util.logging.SimpleLogFormatter;
  10. import gov.usgs.util.logging.StdOutErrLevel;

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

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

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

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

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

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

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

  61.   /** Property for log timestamps. */
  62.   public static final String LOG_TIMESTAMPS_PROPERTY_NAME = "logTimestamps";
  63.   /** Default timestamps is "false". */
  64.   public static final String DEFAULT_TIMESTAMPS = "false";

  65.   /** Property for log directory. */
  66.   public static final String LOGDIRECTORY_PROPERTY_NAME = "logdirectory";
  67.   /** Default log directory is "log". */
  68.   public static final String DEFAULT_LOGDIRECTORY = "log";

  69.   /** Property for log file pattern. */
  70.   public static final String LOGFILE_PROPERTY_NAME = "logfile";
  71.   /** Default log file pattern is "yyyyMMdd'.log'". */
  72.   public static final String DEFAULT_LOGFILE = "yyyyMMdd'.log'";

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

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

  85.   // private static

  86.   /** Private logging object. */
  87.   private static final Logger LOGGER = Logger.getLogger(Bootstrap.class.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 config file to load.
  98.    * @return config
  99.    * @throws IOException if IO error occurs
  100.    */
  101.   public Config loadConfig(final File configFile) throws IOException {
  102.     Config config = new Config();

  103.     // load settings from a config file
  104.     LOGGER.config("Loading configuration file " + configFile.getCanonicalPath());

  105.     try (InputStream in = new FileInputStream(configFile)) {
  106.       config.load(in);
  107.     } catch (FileNotFoundException fex) {
  108.       LOGGER.log(Level.SEVERE, "Unable to find configuration file: " + configFile.getCanonicalPath());
  109.       throw fex;
  110.     }

  111.     return config;
  112.   }

  113.   /**
  114.    * Sets up LogManager
  115.    *
  116.    * @param config Config file
  117.    */
  118.   public void setupLogging(final Config config) {
  119.     final LogManager logManager = LogManager.getLogManager();
  120.     logManager.reset();
  121.     loggers.clear();

  122.     // logging is noisy without this
  123.     for (final String name : new String[] { "com.sun.activation", "com.sun.xml.bind", "javax.xml.bind",
  124.         "org.glassfish.grizzly", "org.glassfish.tyrus", "sun.awt.X11.timeoutTask.XToolkit",
  125.         "jdk.internal.httpclient.websocket.debug", "jdk.internal.httpclient.debug",
  126.         "jdk.internal.httpclient.hpack.debug" }) {
  127.       final Logger logger = Logger.getLogger(name);
  128.       logger.setLevel(Level.INFO);
  129.       // save reference to logger since LogManager uses weakref
  130.       loggers.add(logger);
  131.     }
  132.     ;
  133.     final boolean timestampsEnabled = Boolean
  134.         .parseBoolean(config.getProperty(LOG_TIMESTAMPS_PROPERTY_NAME, DEFAULT_TIMESTAMPS));
  135.     final Level level = Level.parse(config.getProperty(LOGLEVEL_PROPERTY_NAME, DEFAULT_LOGLEVEL));
  136.     final String logDirectory = config.getProperty(LOGDIRECTORY_PROPERTY_NAME, DEFAULT_LOGDIRECTORY);
  137.     LOGGER.config("Logging Level '" + level + "'");
  138.     LOGGER.config("Log directory '" + logDirectory + "'");

  139.     Logger rootLogger = Logger.getLogger("");
  140.     rootLogger.setLevel(level);

  141.     try {
  142.       File logDirectoryFile = new File(logDirectory);
  143.       if (!logDirectoryFile.exists()) {
  144.         LOGGER.fine("Creating log directory");
  145.         if (!logDirectoryFile.mkdirs()) {
  146.           LOGGER.warning("Unable to create log directory");
  147.         }
  148.       }

  149.       // filepattern, maxBytesPerFile, maxFiles, append
  150.       // FileHandler handler = new FileHandler(logFile, 100000, 10, true);
  151.       Handler handler = new SimpleLogFileHandler(logDirectoryFile, new SimpleDateFormat(DEFAULT_LOGFILE));
  152.       handler.setLevel(level);
  153.       rootLogger.addHandler(handler);
  154.     } catch (Exception e) {
  155.       LOGGER.log(Level.WARNING, "Unable to create log file handler", e);
  156.     }

  157.     String redirectConsole = config.getProperty(CONSOLEREDIRECT_PROPERTY_NAME, DEFAULT_CONSOLEREDIRECT);
  158.     if (!redirectConsole.equals(DEFAULT_CONSOLEREDIRECT)) {
  159.       // default is off, so enable
  160.       System.err.println("Redirecting STDOUT and STDERR to log file");
  161.       System.setOut(new PrintStream(new LoggingOutputStream(Logger.getLogger("stdout"), StdOutErrLevel.STDOUT)));
  162.       System.setErr(new PrintStream(new LoggingOutputStream(Logger.getLogger("stderr"), StdOutErrLevel.STDERR)));
  163.     } else {
  164.       ConsoleHandler handler = new ConsoleHandler();
  165.       handler.setLevel(level);
  166.       rootLogger.addHandler(handler);
  167.     }

  168.     Formatter formatter;
  169.     String logFormat = config.getProperty(LOGFORMAT_PROPERTY_NAME, DEFAULT_LOGFORMAT);
  170.     if (logFormat.equals(LOGFORMAT_SIMPLE)) {
  171.       // built in simple formatter
  172.       formatter = new SimpleFormatter();
  173.     } else if (logFormat.equals(LOGFORMAT_XML)) {
  174.       // built in xml formatter
  175.       formatter = new XMLFormatter();
  176.     } else {
  177.       // pdl style simple formatter
  178.       formatter = new SimpleLogFormatter(timestampsEnabled);
  179.     }
  180.     for (Handler handler : rootLogger.getHandlers()) {
  181.       handler.setFormatter(formatter);
  182.     }
  183.   }

  184.   /**
  185.    * command to start application
  186.    *
  187.    * @param args command line arguments
  188.    * @throws Exception on error
  189.    */
  190.   public static void main(final String[] args) throws Exception {
  191.     StringBuffer argumentList = new StringBuffer();
  192.     boolean configTest = false;

  193.     String className = null;

  194.     // use default config file
  195.     File configFile = new File(DEFAULT_CONFIGFILE);
  196.     for (String arg : args) {
  197.       argumentList.append(arg).append(" ");
  198.       if (arg.startsWith(CONFIGFILE_ARGUMENT)) {
  199.         // unless config file argument provided
  200.         configFile = new File(arg.replace(CONFIGFILE_ARGUMENT, ""));
  201.       } else if (arg.equals(CONFIG_TEST_ARGUMENT)) {
  202.         configTest = true;
  203.       } else if (arg.startsWith(MAINCLASS_ARGUMENT)) {
  204.         className = arg.replace(MAINCLASS_ARGUMENT, "");
  205.       } else if (arg.equals(VERSION_ARGUMENT)) {
  206.         System.err.println("Product Distribution Client");
  207.         System.err.println(ProductClient.RELEASE_VERSION);
  208.         System.exit(0);
  209.       }
  210.     }

  211.     Bootstrap bootstrap = new Bootstrap();

  212.     // load configuration file
  213.     Config config = bootstrap.loadConfig(configFile);

  214.     // set global config object
  215.     Config.setConfig(config);

  216.     // setup logging based on configuration
  217.     bootstrap.setupLogging(config);

  218.     // java and os information
  219.     LOGGER.config("java.vendor = " + System.getProperty("java.vendor"));
  220.     LOGGER.config("java.version = " + System.getProperty("java.version"));
  221.     LOGGER.config("java.home = " + System.getProperty("java.home"));
  222.     LOGGER.config("os.arch = " + System.getProperty("os.arch"));
  223.     LOGGER.config("os.name = " + System.getProperty("os.name"));
  224.     LOGGER.config("os.version = " + System.getProperty("os.version"));
  225.     LOGGER.config("user.dir = " + System.getProperty("user.dir"));
  226.     LOGGER.config("user.name = " + System.getProperty("user.name"));

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

  229.     // lookup main class
  230.     if (className == null) {
  231.       // no argument specified, check configuration
  232.       className = config.getProperty(MAINCLASS_PROPERTY_NAME, DEFAULT_MAINCLASS);
  233.     }

  234.     // invoke main class main(String[] args) method
  235.     LOGGER.config("Loading main class " + className);
  236.     Bootstrappable main = null;
  237.     try {
  238.       main = (Bootstrappable) Class.forName(className).getConstructor().newInstance();
  239.     } catch (ClassCastException cce) {
  240.       LOGGER.log(Level.SEVERE, "Main class must implement the Bootstrappable interface", cce);
  241.       System.exit(1);
  242.     }

  243.     // use the configurable interface when available
  244.     if (main instanceof Configurable) {
  245.       Configurable configurable = ((Configurable) main);
  246.       configurable.setName("main");
  247.       try {
  248.         configurable.configure(config);
  249.       } catch (Exception e) {
  250.         LOGGER.log(Level.SEVERE, "Exception loading configuration ", e);
  251.         System.exit(1);
  252.       }
  253.     }

  254.     // configuration loaded okay
  255.     LOGGER.config("Configuration loaded");
  256.     if (configTest) {
  257.       // exit successfully
  258.       System.exit(0);
  259.     }

  260.     // run main instance
  261.     LOGGER.config("Bootstrap complete, running main class\n");
  262.     try {
  263.       main.run(args);
  264.     } catch (Exception e) {
  265.       LOGGER.log(Level.SEVERE, "Main class threw exception, exiting", e);
  266.       System.exit(Bootstrappable.RUN_EXCEPTION_EXIT_CODE);
  267.     }
  268.   }

  269. }