ShakeMapIndexerWedge.java

  1. /*
  2.  * ShakemapIndexerWedge
  3.  */
  4. package gov.usgs.earthquake.shakemap;

  5. import java.io.File;
  6. import java.io.IOException;
  7. import java.util.HashMap;
  8. import java.util.Map;
  9. import java.util.logging.Logger;

  10. import gov.usgs.earthquake.distribution.Command;
  11. import gov.usgs.earthquake.distribution.Command.CommandTimeout;
  12. import gov.usgs.earthquake.distribution.DefaultNotificationListener;
  13. import gov.usgs.earthquake.distribution.NotificationListenerException;
  14. import gov.usgs.earthquake.product.Product;
  15. import gov.usgs.earthquake.product.ProductId;
  16. import gov.usgs.earthquake.product.io.DirectoryProductHandler;
  17. import gov.usgs.earthquake.product.io.DirectoryProductSource;
  18. import gov.usgs.earthquake.product.io.ObjectProductHandler;
  19. import gov.usgs.earthquake.product.io.ObjectProductSource;
  20. import gov.usgs.util.Config;
  21. import gov.usgs.util.FileUtils;

  22. /**
  23.  * Legacy interface to trigger pre-Indexer ShakeMap processing.
  24.  *
  25.  * The Old ShakeMap Indexer is no longer used, and this class is deprecated.
  26.  *
  27.  * When a shakemap product arrives, it is only processed if one of these is
  28.  * true:
  29.  * <ul>
  30.  * <li>doesn't already exist</li>
  31.  * <li>from preferred source (product source = eventsource)</li>
  32.  * <li>from same source as before</li>
  33.  * </ul>
  34.  *
  35.  * When processing a shakemap:
  36.  * <ol>
  37.  * <li>remove previous version</li>
  38.  * <li>unpack new version, if not a delete</li>
  39.  * <li>trigger legacy indexer</li>
  40.  * </ol>
  41.  *
  42.  * Configurable properties:
  43.  * <dl>
  44.  * <dt>indexerCommand</dt>
  45.  * <dd>The shakemap indexer command to run. Defaults to
  46.  * <code>/home/www/vhosts/earthquake/cron/shakemap_indexer.php</code> .</dd>
  47.  *
  48.  * <dt>shakemapDirectory</dt>
  49.  * <dd>The shakemap event directory. Defaults to
  50.  * <code>/home/www/vhosts/earthquake/htdocs/earthquakes/shakemap</code> .</dd>
  51.  *
  52.  * <dt>timeout</dt>
  53.  * <dd>How long in milliseconds the indexer is allowed to run before being
  54.  * terminated.</dd>
  55.  * </dl>
  56.  */
  57. @Deprecated()
  58. public class ShakeMapIndexerWedge extends DefaultNotificationListener {

  59.   /** Logging object. */
  60.   private static final Logger LOGGER = Logger.getLogger(ShakeMapIndexerWedge.class.getName());

  61.   /** Translate from event source to old style shakemap source. */
  62.   private static final Map<String, String> SOURCE_TRANSLATION_MAP = new HashMap<String, String>();
  63.   static {
  64.     SOURCE_TRANSLATION_MAP.put("ci", "sc");
  65.     SOURCE_TRANSLATION_MAP.put("us", "global");
  66.     SOURCE_TRANSLATION_MAP.put("uu", "ut");
  67.     SOURCE_TRANSLATION_MAP.put("uw", "pn");
  68.   }

  69.   /** Configurable property. */
  70.   public static final String SHAKEMAP_INDEXER_COMMAND_PROPERTY = "indexerCommand";

  71.   /** The shakemap indexer command to execute. */
  72.   public static final String DEFAULT_SHAKEMAP_INDEXER_COMMAND = "/home/www/vhosts/earthquake/cron/shakemap_indexer.php";

  73.   /** Configurable property for command timeout. */
  74.   public static final String COMMAND_TIMEOUT_PROPERTY = "timeout";

  75.   /** Default command timeout. */
  76.   public static final String DEFAULT_COMMAND_TIMEOUT = "100000";

  77.   /** Configurable property for shakemap directory. */
  78.   public static final String SHAKEMAP_DIRECTORY_PROPERTY = "shakemapDirectory";

  79.   /** Default shakemap directory. */
  80.   public static final String DEFAULT_SHAKEMAP_DIRECTORY = "/home/www/vhosts/earthquake/htdocs/earthquakes/shakemap";

  81.   /** The indexer command to run. */
  82.   private String indexerCommand = DEFAULT_SHAKEMAP_INDEXER_COMMAND;

  83.   /** Base event directory for shakemap storage. */
  84.   private File baseEventDirectory = new File(DEFAULT_SHAKEMAP_DIRECTORY);

  85.   /** Timeout when running indexer command, in milliseconds. */
  86.   private long indexerTimeout = Long.valueOf(DEFAULT_COMMAND_TIMEOUT);

  87.   /**
  88.    * Create a new ShakeMapIndexerWedge.
  89.    *
  90.    * Sets up the includeTypes list to contain "shakemap".
  91.    */
  92.   public ShakeMapIndexerWedge() {
  93.     this.getIncludeTypes().add("shakemap");
  94.   }

  95.   /**
  96.    * Receive a ShakeMap from Product Distribution.
  97.    *
  98.    * @param product a shakemap type product.
  99.    */
  100.   @Override
  101.   public void onProduct(final Product product) throws Exception {
  102.     ProductId productId = product.getId();

  103.     // convert this product to a ShakeMap product, which has more
  104.     // information
  105.     ShakeMap shakemap = new ShakeMap(product);

  106.     // get the legacy directory
  107.     File legacyDirectory = getEventDirectory(shakemap);

  108.     // check for a previous version of this shakemap
  109.     if (legacyDirectory.exists()) {
  110.       LOGGER.info("Shakemap directory exists " + legacyDirectory.getCanonicalPath());

  111.       try {
  112.         ShakeMap previousShakemap = new ShakeMap(
  113.             ObjectProductHandler.getProduct(new DirectoryProductSource(legacyDirectory)));
  114.         ProductId previousId = previousShakemap.getId();

  115.         // same version?
  116.         if (productId.equals(previousId)) {
  117.           // already have this version of shakemap
  118.           LOGGER.info("Shakemap already processed " + productId.toString());
  119.           return;
  120.         } else {
  121.           LOGGER.info("Shakemap is different, previous is " + previousId.toString());
  122.         }

  123.         if (!productId.getSource().equals(shakemap.getEventSource())
  124.             && !productId.getSource().equals(previousId.getSource())) {
  125.           // incoming is not from preferred source
  126.           LOGGER.info("Skipping non-preferred shakemap, previous source='" + previousId.getSource()
  127.               + "' incoming source='" + productId.getSource() + "'");
  128.           return;
  129.         }
  130.       } catch (Exception e) {
  131.         // unable to load as a product, may be just a shakemap directory
  132.         // received via rsync instead of a shakemap product directory

  133.         if (!productId.getSource().equals(shakemap.getEventSource())) {
  134.           // only process if product source matches event source
  135.           LOGGER
  136.               .info("Shakemap directory already exists, skipping non-preferred source '" + productId.getSource() + "'");
  137.           return;
  138.         }
  139.       }

  140.       // remove previous version
  141.       FileUtils.deleteTree(legacyDirectory);
  142.     }

  143.     // passed filtering, so do what the product says
  144.     String source = translateShakeMapSource(shakemap.getEventSource());
  145.     String code = shakemap.getEventSourceCode();
  146.     boolean delete = false;

  147.     if (!shakemap.isDeleted()) {
  148.       // write the original product, not the modified ShakeMap product.
  149.       new ObjectProductSource(product).streamTo(new DirectoryProductHandler(legacyDirectory));
  150.     } else {
  151.       // need to delete the shakemap, everywhere
  152.       // the indexer will handle the everywhere part...
  153.       delete = true;
  154.     }

  155.     // run the indexer to update shakemap pages
  156.     int exitCode = runIndexer(source, code, delete);
  157.     if (exitCode == 0) {
  158.       return;
  159.     } else {
  160.       throw new NotificationListenerException("[" + getName() + "] command exited with status " + exitCode);
  161.     }
  162.   }

  163.   /**
  164.    * Run the shakemap indexer.
  165.    *
  166.    * If network and code are omitted, all events are updated.
  167.    *
  168.    * @param network the network to update.
  169.    * @param code    the code to update.
  170.    * @param delete  whether indexer is handling a delete (true) or update (false).
  171.    * @return -1 if indexer does not complete within max(1, getAttemptCount())
  172.    *         times, or exit code if indexer completes.
  173.    * @throws IOException if IO error occurs
  174.    */
  175.   public int runIndexer(final String network, final String code, final boolean delete) throws Exception {
  176.     // build indexer command
  177.     StringBuffer updateCommand = new StringBuffer(indexerCommand);
  178.     if (network != null && code != null) {
  179.       updateCommand.append(" --network=").append(network);
  180.       updateCommand.append(" --code=").append(code);
  181.       if (delete) {
  182.         updateCommand.append(" --delete");
  183.       }
  184.     }

  185.     // now run command
  186.     String productCommand = updateCommand.toString();

  187.     Command command = new Command();
  188.     command.setCommand(productCommand);
  189.     command.setTimeout(indexerTimeout);
  190.     try {
  191.       LOGGER.fine("[" + getName() + "] running command '" + productCommand + "'");
  192.       command.execute();

  193.       int exitCode = command.getExitCode();
  194.       LOGGER.info("[" + getName() + "] command '" + productCommand + "' exited with status '" + exitCode + "'");
  195.       return exitCode;
  196.     } catch (CommandTimeout ct) {
  197.       LOGGER.warning("[" + getName() + "] command '" + productCommand + "' timed out");
  198.       return -1;
  199.     }
  200.   }

  201.   /**
  202.    * Get the directory for a particular shakemap.
  203.    *
  204.    * @param shakemap the shakemap to find a directory for.
  205.    * @return the shakemap directory.
  206.    * @throws Exception if error occurs
  207.    */
  208.   public File getEventDirectory(final ShakeMap shakemap) throws Exception {
  209.     String source = translateShakeMapSource(shakemap.getEventSource());
  210.     String code = shakemap.getEventSourceCode();

  211.     return new File(baseEventDirectory, source + "/shake/" + code);
  212.   }

  213.   /**
  214.    * Translate from an event source to the old style shakemap source.
  215.    *
  216.    * Driven by the SOURCE_TRANSLATION_MAP.
  217.    *
  218.    * @param eventSource the event network.
  219.    * @return the shakemap network.
  220.    */
  221.   public String translateShakeMapSource(final String eventSource) {
  222.     if (SOURCE_TRANSLATION_MAP.containsKey(eventSource)) {
  223.       return SOURCE_TRANSLATION_MAP.get(eventSource);
  224.     }
  225.     return eventSource;
  226.   }

  227.   /**
  228.    * Configure this shakemap indexer.
  229.    */
  230.   @Override
  231.   public void configure(final Config config) throws Exception {
  232.     super.configure(config);

  233.     baseEventDirectory = new File(config.getProperty(SHAKEMAP_DIRECTORY_PROPERTY, DEFAULT_SHAKEMAP_DIRECTORY));
  234.     LOGGER.config("Shakemap event directory " + baseEventDirectory.getCanonicalPath());

  235.     indexerCommand = config.getProperty(SHAKEMAP_INDEXER_COMMAND_PROPERTY, DEFAULT_SHAKEMAP_INDEXER_COMMAND);
  236.     LOGGER.config("Shakemap indexer command " + indexerCommand);

  237.     indexerTimeout = Long.valueOf(config.getProperty(COMMAND_TIMEOUT_PROPERTY, DEFAULT_COMMAND_TIMEOUT));
  238.     LOGGER.config("Shakemap indexer command timeout " + indexerTimeout);
  239.   }

  240. }