SearchResponseXmlProductSource.java

  1. /*
  2.  * SearchResponseXmlProductSource
  3.  */
  4. package gov.usgs.earthquake.indexer;

  5. import java.util.logging.Level;
  6. import java.util.logging.Logger;

  7. import org.xml.sax.Attributes;
  8. import org.xml.sax.SAXException;

  9. import gov.usgs.earthquake.distribution.FileProductStorage;
  10. import gov.usgs.earthquake.product.Product;
  11. import gov.usgs.earthquake.product.ProductId;
  12. import gov.usgs.earthquake.product.io.ProductHandler;
  13. import gov.usgs.earthquake.product.io.XmlProductHandler;
  14. import gov.usgs.earthquake.product.io.XmlProductSource;

  15. /**
  16.  * Used by SearchResponseParser to store products during parsing.
  17.  *
  18.  * Creates a "background" storage thread for storing, while this classes
  19.  * startElement, characters, and endElement methods are called by the
  20.  * "foreground" xml parsing thread.
  21.  */
  22. public class SearchResponseXmlProductSource extends XmlProductSource {

  23.     /** Logging object. */
  24.     private static final Logger LOGGER = Logger
  25.             .getLogger(SearchResponseXmlProductSource.class.getName());

  26.     /** The storage where the product is streamed. */
  27.     private FileProductStorage storage;

  28.     /** The stored id. */
  29.     private Product storedProduct = null;

  30.     /** The thread where storage does its thing (current thread is xml parsing). */
  31.     private Thread storageThread;

  32.     /** */
  33.     private final Object waitForSetHandlerSync = new Object();

  34.     /** */
  35.     private final Object waitForStreamToSync = new Object();

  36.     /** start product attributes, for acquiring writelock in background thread */
  37.     private String uri;
  38.     private String localName;
  39.     private String qName;
  40.     private Attributes attributes;
  41.     private SAXException exception;

  42.     /**
  43.      * Construct a SearchResponseXmlProductSource.
  44.      *
  45.      * @param storage
  46.      *            the storage where the parsed product is stored.
  47.      */
  48.     public SearchResponseXmlProductSource(final FileProductStorage storage) {
  49.         super((ProductHandler) null);
  50.         this.setStorage(storage);
  51.     }

  52.     /**
  53.      * Called by the underlying product storage as part os storeProductSource.
  54.      *
  55.      * This method notifies the XML parsing thread that parsing may continue,
  56.      * since the handler is now setup.
  57.      */
  58.     @Override
  59.     public void streamTo(final ProductHandler handler) {
  60.         this.setHandler(handler);

  61.         try {
  62.             // start the product in this thread, so the write lock is acquired
  63.             // and released in the same thread
  64.             super.startElement(uri, localName, qName, attributes);
  65.         } catch (SAXException e) {
  66.             exception = e;
  67.         }
  68.         // clear references that are no longer needed
  69.         this.uri = null;
  70.         this.localName = null;
  71.         this.qName = null;
  72.         this.attributes = null;

  73.         synchronized (waitForSetHandlerSync) {
  74.             // notify xml parsing thread that handler is all set
  75.             waitForSetHandlerSync.notify();
  76.         }

  77.         synchronized (waitForStreamToSync) {
  78.             try {
  79.                 // wait for xml parsing thread to notify streamTo is complete
  80.                 waitForStreamToSync.wait();
  81.             } catch (Exception e) {
  82.                 // ignore
  83.             }
  84.         }
  85.     }

  86.     @Override
  87.     public void startElement(final String uri, final String localName,
  88.             final String qName, final Attributes attributes)
  89.             throws SAXException {
  90.         boolean startElementAlreadySent = false;

  91.         if (uri.equals(XmlProductHandler.PRODUCT_XML_NAMESPACE)
  92.             && localName.equals(XmlProductHandler.PRODUCT_ELEMENT)) {
  93.             // save these to write lock can be acquired by correct thread.
  94.             this.uri = uri;
  95.             this.localName = localName;
  96.             this.qName = qName;
  97.             this.attributes = attributes;

  98.             // starting a product, set up the storage handler/thread
  99.             // reference used by storage thread to set product
  100.             final SearchResponseXmlProductSource thisSource = this;
  101.             storageThread = new Thread() {
  102.                 public void run() {
  103.                     try {
  104.                         ProductId id = storage
  105.                                 .storeProductSource(thisSource);
  106.                         thisSource.setProduct(storage.getProduct(id));
  107.                     } catch (Exception e) {
  108.                         LOGGER.log(Level.WARNING,
  109.                                 "Exception while storing product", e);
  110.                         thisSource.setProduct(null);
  111.                     }
  112.                 }
  113.             };

  114.             synchronized (waitForSetHandlerSync) {
  115.                 storageThread.start();
  116.                 try {
  117.                     // wait for storage thread to call streamTo with
  118.                     // handler
  119.                     waitForSetHandlerSync.wait();
  120.                 } catch (InterruptedException e) {
  121.                     // ignore
  122.                 }
  123.                 // handler set, ready to continue parsing

  124.                 // signal that we've already sent the startElement
  125.                 startElementAlreadySent = true;

  126.                 // if an exception was generated in background thread, throw
  127.                 // it here
  128.                 if (exception != null) {
  129.                     throw exception;
  130.                 }
  131.             }
  132.         }

  133.         if (!startElementAlreadySent) {
  134.             // forward call to parser
  135.             super.startElement(uri, localName, qName, attributes);
  136.         }
  137.     }

  138.     public void endElement(final String uri, final String localName,
  139.             final String qName) throws SAXException {

  140.         // forward call to parser
  141.         super.endElement(uri, localName, qName);

  142.         if (!uri.equals(XmlProductHandler.PRODUCT_XML_NAMESPACE)) {
  143.             return;
  144.         }

  145.         if (localName.equals(XmlProductHandler.PRODUCT_ELEMENT)) {
  146.             // done parsing the product

  147.             synchronized (waitForStreamToSync) {
  148.                 // notify storageThread streamTo is complete
  149.                 waitForStreamToSync.notify();
  150.             }

  151.             try {
  152.                 // wait for storageThread to complete so storage will have
  153.                 // called setProduct before returning
  154.                 storageThread.join();
  155.             } catch (InterruptedException e) {
  156.                 // ignore
  157.             } finally {
  158.                 storageThread = null;
  159.             }
  160.         }
  161.     }

  162.     /** @param storage FileProductStorage to set */
  163.     public void setStorage(FileProductStorage storage) {
  164.         this.storage = storage;
  165.     }

  166.     /** @return FileProductStorage */
  167.     public FileProductStorage getStorage() {
  168.         return storage;
  169.     }

  170.     /**
  171.      * @return the parsed, stored product.
  172.      */
  173.     public Product getProduct() {
  174.         return this.storedProduct;
  175.     }

  176.     /**
  177.      * Method used by storage to provide the parsed product.
  178.      *
  179.      * @param product to set
  180.      */
  181.     protected void setProduct(final Product product) {
  182.         this.storedProduct = product;
  183.     }

  184. }