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.distribution.ProductTracker;
  15. import gov.usgs.earthquake.product.Product;
  16. import gov.usgs.earthquake.product.ProductId;
  17. import gov.usgs.earthquake.product.io.DirectoryProductHandler;
  18. import gov.usgs.earthquake.product.io.DirectoryProductSource;
  19. import gov.usgs.earthquake.product.io.ObjectProductHandler;
  20. import gov.usgs.earthquake.product.io.ObjectProductSource;
  21. import gov.usgs.util.Config;
  22. import gov.usgs.util.FileUtils;

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

  62.     /** Logging object. */
  63.     private static final Logger LOGGER = Logger
  64.             .getLogger(ShakeMapIndexerWedge.class.getName());

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

  73.     /** Configurable property. */
  74.     public static final String SHAKEMAP_INDEXER_COMMAND_PROPERTY = "indexerCommand";

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

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

  79.     /** Default command timeout. */
  80.     public static final String DEFAULT_COMMAND_TIMEOUT = "100000";

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

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

  85.     /** The indexer command to run. */
  86.     private String indexerCommand = DEFAULT_SHAKEMAP_INDEXER_COMMAND;

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

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

  91.     /**
  92.      * Create a new ShakeMapIndexerWedge.
  93.      *
  94.      * Sets up the includeTypes list to contain "shakemap".
  95.      */
  96.     public ShakeMapIndexerWedge() {
  97.         this.getIncludeTypes().add("shakemap");
  98.     }

  99.     /**
  100.      * Receive a ShakeMap from Product Distribution.
  101.      *
  102.      * @param product
  103.      *            a shakemap type product.
  104.      */
  105.     @Override
  106.     public void onProduct(final Product product) throws Exception {
  107.         ProductId productId = product.getId();

  108.         // convert this product to a ShakeMap product, which has more
  109.         // information
  110.         ShakeMap shakemap = new ShakeMap(product);

  111.         // get the legacy directory
  112.         File legacyDirectory = getEventDirectory(shakemap);

  113.         // check for a previous version of this shakemap
  114.         if (legacyDirectory.exists()) {
  115.             LOGGER.info("Shakemap directory exists "
  116.                     + legacyDirectory.getCanonicalPath());

  117.             try {
  118.                 ShakeMap previousShakemap = new ShakeMap(
  119.                         ObjectProductHandler
  120.                                 .getProduct(new DirectoryProductSource(
  121.                                         legacyDirectory)));
  122.                 ProductId previousId = previousShakemap.getId();

  123.                 // same version?
  124.                 if (productId.equals(previousId)) {
  125.                     // already have this version of shakemap
  126.                     LOGGER.info("Shakemap already processed "
  127.                             + productId.toString());
  128.                     return;
  129.                 } else {
  130.                     LOGGER.info("Shakemap is different, previous is "
  131.                             + previousId.toString());
  132.                 }

  133.                 if (!productId.getSource().equals(shakemap.getEventSource())
  134.                         && !productId.getSource().equals(previousId.getSource())) {
  135.                     // incoming is not from preferred source
  136.                     LOGGER.info("Skipping non-preferred shakemap, previous source='"
  137.                             + previousId.getSource()
  138.                             + "' incoming source='"
  139.                             + productId.getSource()
  140.                             + "'");
  141.                     return;
  142.                 }
  143.             } catch (Exception e) {
  144.                 // unable to load as a product, may be just a shakemap directory
  145.                 // received via rsync instead of a shakemap product directory

  146.                 if (!productId.getSource().equals(shakemap.getEventSource())) {
  147.                     // only process if product source matches event source
  148.                     LOGGER.info("Shakemap directory already exists, skipping non-preferred source '"
  149.                             + productId.getSource() + "'");
  150.                     return;
  151.                 }
  152.             }

  153.             // remove previous version
  154.             FileUtils.deleteTree(legacyDirectory);
  155.         }

  156.         // passed filtering, so do what the product says
  157.         String source = translateShakeMapSource(shakemap.getEventSource());
  158.         String code = shakemap.getEventSourceCode();
  159.         boolean delete = false;

  160.         if (!shakemap.isDeleted()) {
  161.             // write the original product, not the modified ShakeMap product.
  162.             new ObjectProductSource(product)
  163.                     .streamTo(new DirectoryProductHandler(legacyDirectory));
  164.         } else {
  165.             // need to delete the shakemap, everywhere
  166.             // the indexer will handle the everywhere part...
  167.             delete = true;
  168.         }

  169.         // run the indexer to update shakemap pages
  170.         int exitCode = runIndexer(source, code, delete);
  171.         if (exitCode == 0) {
  172.             new ProductTracker(product.getTrackerURL()).productIndexed(
  173.                     this.getName(), productId);
  174.         } else {
  175.             throw new NotificationListenerException("[" + getName()
  176.                     + "] command exited with status " + exitCode);
  177.         }
  178.     }

  179.     /**
  180.      * Run the shakemap indexer.
  181.      *
  182.      * If network and code are omitted, all events are updated.
  183.      *
  184.      * @param network
  185.      *            the network to update.
  186.      * @param code
  187.      *            the code to update.
  188.      * @param delete
  189.      *            whether indexer is handling a delete (true) or update (false).
  190.      * @return -1 if indexer does not complete within max(1, getAttemptCount())
  191.      *         times, or exit code if indexer completes.
  192.      * @throws IOException if IO error occurs
  193.      */
  194.     public int runIndexer(final String network, final String code,
  195.             final boolean delete) throws Exception {
  196.         // build indexer command
  197.         StringBuffer updateCommand = new StringBuffer(indexerCommand);
  198.         if (network != null && code != null) {
  199.             updateCommand.append(" --network=").append(network);
  200.             updateCommand.append(" --code=").append(code);
  201.             if (delete) {
  202.                 updateCommand.append(" --delete");
  203.             }
  204.         }

  205.         // now run command
  206.         String productCommand = updateCommand.toString();

  207.         Command command = new Command();
  208.         command.setCommand(productCommand);
  209.         command.setTimeout(indexerTimeout);
  210.         try {
  211.             LOGGER.fine("[" + getName() + "] running command '"
  212.                     + productCommand + "'");
  213.             command.execute();

  214.             int exitCode = command.getExitCode();
  215.             LOGGER.info("[" + getName() + "] command '" + productCommand
  216.                     + "' exited with status '" + exitCode + "'");
  217.             return exitCode;
  218.         } catch (CommandTimeout ct) {
  219.             LOGGER.warning("[" + getName() + "] command '" + productCommand
  220.                     + "' timed out");
  221.             return -1;
  222.         }
  223.     }

  224.     /**
  225.      * Get the directory for a particular shakemap.
  226.      *
  227.      * @param shakemap
  228.      *            the shakemap to find a directory for.
  229.      * @return the shakemap directory.
  230.      * @throws Exception if error occurs
  231.      */
  232.     public File getEventDirectory(final ShakeMap shakemap) throws Exception {
  233.         String source = translateShakeMapSource(shakemap.getEventSource());
  234.         String code = shakemap.getEventSourceCode();

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

  237.     /**
  238.      * Translate from an event source to the old style shakemap source.
  239.      *
  240.      * Driven by the SOURCE_TRANSLATION_MAP.
  241.      *
  242.      * @param eventSource
  243.      *            the event network.
  244.      * @return the shakemap network.
  245.      */
  246.     public String translateShakeMapSource(final String eventSource) {
  247.         if (SOURCE_TRANSLATION_MAP.containsKey(eventSource)) {
  248.             return SOURCE_TRANSLATION_MAP.get(eventSource);
  249.         }
  250.         return eventSource;
  251.     }

  252.     /**
  253.      * Configure this shakemap indexer.
  254.      */
  255.     @Override
  256.     public void configure(final Config config) throws Exception {
  257.         super.configure(config);

  258.         baseEventDirectory = new File(config.getProperty(
  259.                 SHAKEMAP_DIRECTORY_PROPERTY, DEFAULT_SHAKEMAP_DIRECTORY));
  260.         LOGGER.config("Shakemap event directory "
  261.                 + baseEventDirectory.getCanonicalPath());

  262.         indexerCommand = config.getProperty(SHAKEMAP_INDEXER_COMMAND_PROPERTY,
  263.                 DEFAULT_SHAKEMAP_INDEXER_COMMAND);
  264.         LOGGER.config("Shakemap indexer command " + indexerCommand);

  265.         indexerTimeout = Long.valueOf(config.getProperty(
  266.                 COMMAND_TIMEOUT_PROPERTY, DEFAULT_COMMAND_TIMEOUT));
  267.         LOGGER.config("Shakemap indexer command timeout " + indexerTimeout);
  268.     }

  269. }