ProductClient.java

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

  5. import gov.usgs.earthquake.indexer.SearchCLI;

  6. import gov.usgs.util.Config;
  7. import gov.usgs.util.DefaultConfigurable;
  8. import gov.usgs.util.StringUtils;

  9. import java.lang.management.ManagementFactory;

  10. import java.util.Iterator;
  11. import java.util.List;
  12. import java.util.LinkedList;
  13. import java.util.logging.Level;
  14. import java.util.logging.Logger;

  15. import javax.management.MBeanServer;
  16. import javax.management.ObjectName;

  17. /**
  18.  * The entry point to product distribution.
  19.  *
  20.  * The ProductClient implements the Configurable interface and can use the
  21.  * following configuration parameters:
  22.  *
  23.  * <dl>
  24.  *
  25.  * <dt>receivers</dt>
  26.  * <dd>(Required) A comma separated list of section names that should be loaded
  27.  * as NotificationReceiver objects. Each receiver is a source of notifications
  28.  * for listeners.</dd>
  29.  *
  30.  * <dt>listeners</dt>
  31.  * <dd>(Required) A comma separated list of section names that should be loaded
  32.  * as NotificationListener objects. Each listener receives notifications from
  33.  * receivers.</dd>
  34.  *
  35.  * <dt>logdirectory</dt>
  36.  * <dd>(Optional) Log directory. Default is "log", relative to the current
  37.  * working directory. Log files using a naming convention
  38.  * <code>ProductClient_YYYYMMDD.log</code>.</dd>
  39.  *
  40.  * <dt>loglevel</dt> <dd>(Optional) Default is INFO. One of SEVERE, WARNING,
  41.  * INFO, CONFIG, FINE, FINER, FINEST</dd>
  42.  *
  43.  * <dt>enableTracker</dt> <dd>(Optional) Default is false. Whether or not to
  44.  * send tracker updates to a product tracker. This is generally desirable, but
  45.  * is disabled by default for the paranoid.</dd>
  46.  *
  47.  * <dt>redirectconsole</dt> <dd>(Optional) Default is false. Whether or not to
  48.  * redirect console output to the log file.</dd>
  49.  *
  50.  * </dl>
  51.  *
  52.  * <p>
  53.  * All listeners listen to all receivers for notifications.
  54.  * </p>
  55.  *
  56.  */
  57. public class ProductClient extends DefaultConfigurable implements
  58.         ProductClientMBean, Bootstrappable {

  59.     /** The "release" version number. */
  60.     public static final String RELEASE_VERSION = "Version 2.8.0 2022-10-11";

  61.     /** Property name used on products for current RELEASE_VERSION. */
  62.     public static final String PDL_CLIENT_VERSION_PROPERTY = "pdl-client-version";

  63.     /** SVN Id property. */
  64.     public static final String SVN_VERSION = "$Id$";

  65.     /** SVN Revision property. */
  66.     public static final String SVN_REVISION = "$Revision$";

  67.     /** SVN LastChangedDate property. */
  68.     public static final String SVN_LAST_CHANGED_DATE = "$LastChangedDate$";

  69.     /** Logging object. */
  70.     private static final Logger LOGGER = Logger.getLogger(ProductClient.class
  71.             .getName());

  72.     /** Defaults are loaded from inside the jar file. */
  73.     public static final String JAR_CONFIG_FILE = "etc/config/config.ini";

  74.     /** Default location of config file. */
  75.     public static final String DEFAULT_CONFIG_FILE = "config.ini";

  76.     /** Custom config file locations. */
  77.     public static final String CONFIG_FILE_ARGUMENT = "--configFile=";

  78.     /** Run the builder. */
  79.     public static final String BUILD_ARGUMENT = "--build";

  80.     /** Run the tracker. */
  81.     public static final String TRACK_ARGUMENT = "--track";

  82.     /** Run the builder. */
  83.     public static final String SEND_ARGUMENT = "--send";

  84.     /** Run the client. */
  85.     public static final String RECEIVE_ARGUMENT = "--receive";

  86.     /** Run the search. */
  87.     public static final String SEARCH_ARGUMENT = "--search";

  88.     /** Show usage. */
  89.     public static final String USAGE_ARGUMENT = "--help";

  90.     /** Property containing list of receivers. */
  91.     public static final String RECEIVERS_PROPERTY_NAME = "receivers";

  92.     /** Property containing list of senders. */
  93.     public static final String LISTENERS_PROPERTY_NAME = "listeners";

  94.     /** Log level property name. */
  95.     public static final String LOGLEVEL_PROPERTY_NAME = "loglevel";

  96.     /** Default log level is INFO. */
  97.     public static final String DEFAULT_LOGLEVEL = "INFO";

  98.     /** Property with location for log directory. */
  99.     public static final String LOGDIRECTORY_PROPERTY_NAME = "logdirectory";

  100.     /** Default directory for logging. */
  101.     public static final String DEFAULT_LOGDIRECTORY = "log";

  102.     /** Default location for log file. */
  103.     public static final String DEFAULT_LOGFILE = "'ProductClient'_yyyyMMdd'.log'";

  104.     /** Whether or not to redirect stdout and stderr to log file. */
  105.     public static final String CONSOLE_REDIRECT_PROPERTY_NAME = "redirectconsole";

  106.     /** Default console redirect value (don't redirect). */
  107.     public static final String DEFAULT_CONSOLE_REDIRECT = "false";

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

  110.     /** Property used to enable Admin Socket */
  111.     public static final String ENABLE_ADMIN_SOCKET = "enableAdminSocket";
  112.     /** Default bool for admin socket property */
  113.     public static final String DEFAULT_ENABLE_ADMIN_SOCKET = "false";

  114.     /** List of receivers that generate notifications. */
  115.     private List<NotificationReceiver> receivers = new LinkedList<NotificationReceiver>();

  116.     /** List of listeners that receive notifications. */
  117.     private List<NotificationListener> listeners = new LinkedList<NotificationListener>();

  118.     /** Whether to start a zabbix agent. */
  119.     private boolean enableAdminSocket = false;
  120.     private boolean enableJMX = true;
  121.     private AdminSocketServer adminSocketServer = null;

  122.     public void configure(Config config) throws Exception {
  123.         loadListeners(config);
  124.         loadReceivers(config);

  125.         // connect all listeners to all receivers
  126.         Iterator<NotificationReceiver> iter = receivers.iterator();
  127.         while (iter.hasNext()) {
  128.             NotificationReceiver receiver = iter.next();
  129.             Iterator<NotificationListener> iter2 = listeners.iterator();
  130.             while (iter2.hasNext()) {
  131.                 NotificationListener listener = iter2.next();
  132.                 receiver.addNotificationListener(listener);
  133.             }
  134.         }

  135.         enableAdminSocket = Boolean.valueOf(config.getProperty(
  136.                 ENABLE_ADMIN_SOCKET, DEFAULT_ENABLE_ADMIN_SOCKET));
  137.     }

  138.     /**
  139.      * Load listeners from a Config object.
  140.      *
  141.      * @param config
  142.      *            the configuration.
  143.      * @throws Exception if error occurs
  144.      */
  145.     public void loadListeners(final Config config) throws Exception {
  146.         Iterator<String> iter = StringUtils.split(
  147.                 config.getProperty(LISTENERS_PROPERTY_NAME, ""), ",")
  148.                 .iterator();
  149.         while (iter.hasNext()) {
  150.             String listenerName = iter.next();
  151.             LOGGER.config("Loading listener '" + listenerName + "'");

  152.             NotificationListener listener = (NotificationListener) Config
  153.                     .getConfig().getObject(listenerName);
  154.             if (listener == null) {
  155.                 throw new ConfigurationException("Unable to load listener '"
  156.                         + listenerName
  157.                         + "', make sure it is properly configured.");
  158.             }

  159.             // listenerName references an object in the global configuration
  160.             listeners.add(listener);
  161.         }
  162.     }

  163.     /**
  164.      * Load NotificationReceivers from a Config object.
  165.      *
  166.      * @param config
  167.      *            the configuration
  168.      * @throws Exception if error occurs
  169.      */
  170.     public void loadReceivers(final Config config) throws Exception {
  171.         Iterator<String> iter = StringUtils.split(
  172.                 config.getProperty(RECEIVERS_PROPERTY_NAME), ",").iterator();
  173.         while (iter.hasNext()) {
  174.             String receiverName = iter.next();
  175.             LOGGER.config("Loading receiver '" + receiverName + "'");

  176.             NotificationReceiver receiver = (NotificationReceiver) Config
  177.                     .getConfig().getObject(receiverName);
  178.             if (receiver == null) {
  179.                 throw new ConfigurationException("Unable to load receiver '"
  180.                         + receiverName
  181.                         + "', make sure it is properly configured.");
  182.             }
  183.             // receiverName references an object in the global configuration
  184.             receivers.add(receiver);
  185.         }
  186.     }

  187.     /**
  188.      * Start up all listeners and receivers.
  189.      */
  190.     public void startup() throws Exception {
  191.         Iterator<NotificationListener> iter = listeners.iterator();
  192.         while (iter.hasNext()) {
  193.             iter.next().startup();
  194.         }
  195.         Iterator<NotificationReceiver> iter2 = receivers.iterator();
  196.         while (iter2.hasNext()) {
  197.             iter2.next().startup();
  198.         }

  199.         if (enableAdminSocket) {
  200.             LOGGER.info("Starting AdminSocketServer on port 11111");
  201.             adminSocketServer = new AdminSocketServer();
  202.             adminSocketServer.setClient(this);
  203.             adminSocketServer.startup();
  204.         }

  205.         if (enableJMX) {
  206.             MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
  207.             ObjectName clientName = new ObjectName("ProductClient:name=jmx");
  208.             mbs.registerMBean(this, clientName);
  209.         }
  210.     }

  211.     /**
  212.      * Shut down all receivers and listeners.
  213.      */
  214.     public void shutdown() throws Exception {
  215.         if (receivers.size() > 0) {
  216.             Iterator<NotificationReceiver> iter2 = receivers.iterator();
  217.             while (iter2.hasNext()) {
  218.                 try {
  219.                     iter2.next().shutdown();
  220.                 } catch (Exception e) {
  221.                     // ignore
  222.                 }
  223.             }
  224.         }

  225.         if (listeners.size() > 0) {
  226.             Iterator<NotificationListener> iter = listeners.iterator();
  227.             while (iter.hasNext()) {
  228.                 try {
  229.                     iter.next().shutdown();
  230.                 } catch (Exception e) {
  231.                     // ignore
  232.                 }
  233.             }
  234.         }

  235.         if (adminSocketServer != null) {
  236.             try {
  237.                 adminSocketServer.shutdown();
  238.             } catch (Exception e) {
  239.             }
  240.             adminSocketServer = null;
  241.         }
  242.     }

  243.     /**
  244.      * Entry point into Product Distribution.
  245.      *
  246.      * @param args argument
  247.      */
  248.     public void run(final String[] args) throws Exception {
  249.         try {
  250.             // default is show usage
  251.             boolean receiveProducts = false;
  252.             boolean buildProduct = false;
  253.             boolean trackProduct = false;
  254.             boolean searchProduct = false;
  255.             boolean showUsage = false;

  256.             // parse arguments
  257.             for (String arg : args) {
  258.                 if (arg.equals(SEND_ARGUMENT) || arg.equals(BUILD_ARGUMENT)) {
  259.                     buildProduct = true;
  260.                 } else if (arg.equals(RECEIVE_ARGUMENT)) {
  261.                     receiveProducts = true;
  262.                 } else if (arg.equals(TRACK_ARGUMENT)) {
  263.                     trackProduct = true;
  264.                 } else if (arg.equals(SEARCH_ARGUMENT)) {
  265.                     searchProduct = true;
  266.                 } else if (arg.equals(USAGE_ARGUMENT)) {
  267.                     showUsage = true;
  268.                 }
  269.             }

  270.             // output current version
  271.             System.err.println("Product Distribution Client");
  272.             System.err.println(RELEASE_VERSION);
  273.             System.err.println();

  274.             if (buildProduct) {
  275.                 if (showUsage) {
  276.                     System.err.println("Usage: ");
  277.                     System.err
  278.                             .println("    java -jar ProductClient.jar --build [BUILD ARGUMENTS]");
  279.                     System.err.println();
  280.                     System.err.println(CLIProductBuilder.getUsage());
  281.                     System.exit(0);
  282.                 }
  283.                 LOGGER.info("Running Product Builder");
  284.                 // run builder main
  285.                 CLIProductBuilder.main(args);
  286.                 System.exit(0);
  287.             } else if (trackProduct) {
  288.                 if (showUsage) {
  289.                     System.err.println("Usage: ");
  290.                     System.err
  291.                             .println("    java -jar ProductClient.jar --track [TRACK ARGUMENTS]");
  292.                     System.err.println();
  293.                     System.err.println(ProductTracker.getUsage());
  294.                     System.exit(0);
  295.                 }
  296.                 LOGGER.info("Running Product Tracker");
  297.                 ProductTracker.main(args);
  298.                 System.exit(0);
  299.             } else if (searchProduct) {
  300.                 // search needs to happen after track, since track also uses a
  301.                 // --search argument
  302.                 if (showUsage) {
  303.                     System.err.println("Usage: ");
  304.                     System.err
  305.                             .println("    java -jar ProductClient.jar --search [SEARCH ARGUMENTS]");
  306.                     System.err.println();
  307.                     System.err.println(SearchCLI.getUsage());
  308.                     System.exit(0);
  309.                 }
  310.                 LOGGER.info("Running Product Search");
  311.                 SearchCLI.main(args);
  312.                 System.exit(0);
  313.             } else if (receiveProducts && !showUsage) {
  314.                 // start processing
  315.                 LOGGER.info("Starting");
  316.                 try {
  317.                     startup();
  318.                 } catch (Exception e) {
  319.                     LOGGER.log(Level.SEVERE,
  320.                             "Exceptions while starting, shutting down", e);
  321.                     try {
  322.                         // this has been throwing exceptions, move into try
  323.                         shutdown();
  324.                     } finally {
  325.                         // exit no matter what
  326.                         System.exit(1);
  327.                     }
  328.                 }
  329.                 LOGGER.info("Started");

  330.                 // shutdown threads when control-c is pressed
  331.                 // otherwise, would continue running
  332.                 Runtime.getRuntime().addShutdownHook(new Thread() {
  333.                     public void run() {
  334.                         try {
  335.                             LOGGER.info("Shutting down");
  336.                             shutdown();
  337.                             LOGGER.info("Shutdown complete");
  338.                         } catch (Exception e) {
  339.                             LOGGER.log(Level.WARNING,
  340.                                     "Exception while shutting down", e);
  341.                         }
  342.                     }
  343.                 });
  344.             } else {
  345.                 System.err.println("Usage: ");
  346.                 System.err
  347.                         .println("    java -jar ProductClient.jar [ARGUMENTS]");
  348.                 System.err.println();
  349.                 System.err.println(getUsage());
  350.                 System.exit(1);
  351.             }

  352.         } catch (Exception e) {
  353.             LOGGER.log(Level.SEVERE, "Exception in main", e);
  354.         }
  355.     }

  356.     /**
  357.      * @return The list of receivers
  358.      */
  359.     public List<NotificationReceiver> getReceivers() {
  360.         return receivers;
  361.     }

  362.     /**
  363.      *
  364.      * @return The list of listeners
  365.      */
  366.     public List<NotificationListener> getListeners() {
  367.         return listeners;
  368.     }

  369.     /** @return Product usage */
  370.     public static String getUsage() {
  371.         StringBuffer buf = new StringBuffer();

  372.         buf.append("[--configFile=FILE]      override the default config file location\n");
  373.         buf.append("                         default is config.ini in CWD\n");
  374.         buf.append("[--help]                 show this message and exit\n");
  375.         buf.append("[--version]              show the version and exit\n");
  376.         buf.append("[--configTest]           load configuration and exit\n");
  377.         buf.append("\n");
  378.         buf.append("[--send]                 create and send a product\n");
  379.         buf.append("                         try --send --help for more information\n");
  380.         buf.append("[--receive]              receive products\n");
  381.         buf.append("[--track]                check or update product status\n");
  382.         buf.append("                         try --track --help for more information\n");
  383.         buf.append("\n");
  384.         buf.append("You must use one of \"--send\", \"--receive\", or \"--track\"\n");
  385.         buf.append("\n");

  386.         return buf.toString();
  387.     }

  388.     @Override
  389.     public String getListenerQueueStatus() {
  390.         StringBuffer buf = new StringBuffer();
  391.         Iterator<NotificationReceiver> iter = receivers.iterator();
  392.         while (iter.hasNext()) {
  393.             NotificationReceiver receiver = iter.next();
  394.             if (receiver instanceof DefaultNotificationReceiver) {
  395.                 buf.append(((DefaultNotificationReceiver) receiver)
  396.                         .getListenerQueueStatus());
  397.             }
  398.         }
  399.         return buf.toString();
  400.     }

  401.     @Override
  402.     public String getVersion() {
  403.         return RELEASE_VERSION;
  404.     }

  405.     @Override
  406.     public long getMaxMemory() {
  407.         return Runtime.getRuntime().maxMemory();
  408.     }

  409.     @Override
  410.     public long getFreeMemory() {
  411.         return Runtime.getRuntime().freeMemory();
  412.     }

  413. }