Package org.xmlBlaster.engine.query.plugins

Source Code of org.xmlBlaster.engine.query.plugins.QueueQueryPlugin$WaitingQuery

/*------------------------------------------------------------------------------
Name:      QueueQueryPlugin.java
Project:   xmlBlaster.org
Copyright: xmlBlaster.org, see xmlBlaster-LICENSE file
------------------------------------------------------------------------------*/

package org.xmlBlaster.engine.query.plugins;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.xmlBlaster.engine.query.I_Query;
import org.xmlBlaster.engine.queuemsg.MsgQueueUpdateEntry;
import org.xmlBlaster.engine.queuemsg.ReferenceEntry;
import org.xmlBlaster.util.Global;
import org.xmlBlaster.util.MsgUnit;
import org.xmlBlaster.util.StringPairTokenizer;
import org.xmlBlaster.util.XmlBlasterException;
import org.xmlBlaster.util.def.ErrorCode;
import org.xmlBlaster.util.dispatch.DispatchManager;
import org.xmlBlaster.util.qos.ClientProperty;
import org.xmlBlaster.util.qos.MsgQosData;
import org.xmlBlaster.util.queue.I_Entry;
import org.xmlBlaster.util.queue.I_Queue;
import org.xmlBlaster.util.queue.I_Storage;
import org.xmlBlaster.util.queue.I_StorageSizeListener;
import org.xmlBlaster.util.queuemsg.MsgQueueEntry;

import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch;
import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;

/**
* Each TopicHandler/SessionInfo or SubjectInfo instance creates its own instance of this plugin.
* @see <a href="http://www.xmlBlaster.org/xmlBlaster/doc/requirements/engine.qos.queryspec.html">The engine.qos.queryspec requirement</a>
* @see <a href="http://www.xmlBlaster.org/xmlBlaster/doc/requirements/engine.qos.queryspec.QueueQuery.html">The engine.qos.queryspec.QueueQuery requirement</a>
* @author <a href="mailto:michele@laghi.eu">Michele Laghi</a>
*/
public class QueueQueryPlugin implements I_Query, I_StorageSizeListener {
  
   /** Helper container */
   class WaitingQuery {
      /**
       * The maximum number of entries for which to wait. If negative
       * no restriction is given, so the the other limitations count.
       */
      int maxEntries;
      /**
       * The maximum number of bytes which need to be in the queue before
       * it will continue. If negative no restriction is given.
       */
      long maxSize;
      CountDownLatch startSignal = new CountDownLatch(1);
     
      public WaitingQuery(int maxEntries, long maxSize) {
         super();
         this.maxEntries = maxEntries;
         this.maxSize = maxSize;
      }
   }

   private final static String ME = "QueueQueryPlugin";
   private Global global;
   private static Logger log = Logger.getLogger(QueueQueryPlugin.class.getName());
   private Set waitingThreads = new HashSet();
   //private int maxEntries;
   //private long maxSize;
  
   public QueueQueryPlugin(Global global) {
      this.global = global;

   }

   private WaitingQuery[] getWaitingQueries() {
      synchronized (this.waitingThreads) {
         return (WaitingQuery[])this.waitingThreads.toArray(new WaitingQuery[this.waitingThreads.size()]);
      }
   }

   /**
    * If no restriction is given, i.e. if both maxEntries and maxBytes is negative,
    * then it will wait.
    *
    * @return true if it has to wait, false if there are already sufficently entries
    *         in the queue.
    */
   private final boolean checkIfNeedsWaiting(int entriesInQueue, long bytesInQueue, WaitingQuery wq) {
      if (wq.maxEntries > 0) {
         if (entriesInQueue >= wq.maxEntries) return false;
      }
      if (wq.maxSize > 0) {
         if (bytesInQueue >= wq.maxSize) return false;
      }
      return true;
   }
  
   /**
    * The query to the queue. The parameters specifying which kind of query it is
    * are specified in the qos, and more precisely in the QuerySpecQos.
    * @param source must be an I_Queue implementation (can not be null).
    * @param query must not be null, e.g.
    * "maxEntries=3&maxSize=1000&consumable=true&waitingDelay=1000"
    * for example from qosData.getQuerySpecArr()[0].getQuery().getQuery()    
    */
   public MsgUnit[] query(Object source, String query) throws XmlBlasterException {
      // If the SOCKET shuts down it sends a InterruptException to this thread so it
      // falls out of await() and is not leaked. What about CORBA/XMLRPC?
     
      //if (log.isLoggable(Level.FINER)) log.call(ME, "query for '" + keyData.getOid() + "'");
      if (source == null)
         throw new XmlBlasterException(this.global, ErrorCode.INTERNAL_ILLEGALARGUMENT, ME, "The source on which do the query is null");
      if (! (source instanceof I_Queue) )
         throw new XmlBlasterException(this.global, ErrorCode.INTERNAL_ILLEGALARGUMENT, ME, "Wrong type of source for query. Expected an 'I_Queue' implementation but was '" + source.getClass().getName() + "'");
      if (query == null)
         throw new XmlBlasterException(this.global, ErrorCode.INTERNAL_ILLEGALARGUMENT, ME, "The query string is null");
        
      I_Queue queue = (I_Queue)source;        


      int maxEntries = 1;
      long maxSize = -1L;
      boolean consumable = false;
      long waitingDelay = 0L; // no wait is default
     
      // get the query properties
      //if (querySpec.getQuery() != null) query = querySpec.getQuery().getQuery();
      // "maxEntries=3&maxSize=1000&consumable=true&waitingDelay=1000"     
      Map props = StringPairTokenizer.parseToStringClientPropertyPairs(query, "&", "=");
      ClientProperty prop = (ClientProperty)props.get("maxEntries");
      if (prop != null) maxEntries = prop.getIntValue();
      prop = (ClientProperty)props.get("maxSize");
      if (prop != null) maxSize = prop.getLongValue();
      if (maxSize > -1L) {
         log.warning(" Query specification of maxSize is not implemented, please use the default value -1 or leave it untouched: '" + query + "'");
         throw new XmlBlasterException(this.global, ErrorCode.USER_ILLEGALARGUMENT, ME, "Query specification of maxSize is not implemented, please use the default value -1 or leave it untouched");
      }
      prop = (ClientProperty)props.get("consumable");
      if (prop != null) consumable = prop.getBooleanValue();
      prop = (ClientProperty)props.get("waitingDelay");
      if (prop != null) waitingDelay = prop.getLongValue();

      if (log.isLoggable(Level.FINE))
      log.fine("query: waitingDelay='" + waitingDelay + "' consumable='" + consumable + "' maxEntries='" + maxEntries + "' maxSize='" + maxSize + "'");
     
      if (waitingDelay != 0L) {
         if (log.isLoggable(Level.FINE)) log.fine("query: waiting delay is " + waitingDelay);
         if (maxEntries < 1 && maxSize < 1L && waitingDelay < 0L) {
            log.warning("If you specify a blocking get you must also specify a maximum size or maximum number of entries to retreive, otherwise specify non-blocking by setting 'waitingDelay' to zero, query is illegal: '" + query + "'");
            throw new XmlBlasterException(this.global, ErrorCode.USER_ILLEGALARGUMENT, ME, "If you specify a blocking get you must also specify a maximum size or maximum number of entries to retreive, otherwise specify non-blocking by setting 'waitingDelay' to zero");
         }
        
         WaitingQuery wq = new WaitingQuery(maxEntries, maxSize);
        
         if (checkIfNeedsWaiting((int)queue.getNumOfEntries(), queue.getNumOfBytes(), wq)) {
            if (log.isLoggable(Level.FINE)) log.fine("query: going to wait due to first check");
            try {
               synchronized (this.waitingThreads) {
                  this.waitingThreads.add(wq);
               }
               queue.addStorageSizeListener(this);
               boolean timedOut = false;
               try {
                  if (waitingDelay < 0L)
                     wq.startSignal.await();
                  else
                     timedOut = !wq.startSignal.await(waitingDelay, TimeUnit.MILLISECONDS);
                  if (log.isLoggable(Level.FINE)) log.fine("just waked up after waiting for incoming entries, timedOut=" + timedOut);
               }
               catch (InterruptedException ex) {
                  if (log.isLoggable(Level.FINE)) log.fine("just waked up because of interrupt: " + ex.toString());
               }
            }
            catch (Throwable e) {
               log.severe("Can't handle query: " + e.toString());
            }
            finally {
               try {
                  synchronized (this.waitingThreads) {
                     this.waitingThreads.remove(wq);
                  }
                  queue.removeStorageSizeListener(this);
                  if (log.isLoggable(Level.FINE)) log.fine("query: removed myself as a QueueSizeListener");
               }
               catch (Throwable ex) {
                  ex.printStackTrace();
                  log.severe("query: exception occurred when removing the QueueSizeListener from the queue:" + ex.toString());
               }
            }
         }
      }
     
      List<I_Entry> list = queue.peek(maxEntries, maxSize);
      ArrayList entryListChecked = DispatchManager.prepareMsgsFromQueue(ME, log, queue, list);
     
      MsgQueueEntry[] entries = (MsgQueueEntry[])entryListChecked.toArray(new MsgQueueEntry[entryListChecked.size()]);
     
      ArrayList ret = new ArrayList(entries.length);
      for (int i=0; i < entries.length; i++) {
         // TODO: REQ engine.qos.update.queue states that the queue size is passed and not the curr msgArr.length
         ReferenceEntry entry = (ReferenceEntry)entries[i];
         MsgUnit mu = entry.getMsgUnitOrNull();
         if (mu == null)
            continue;
         MsgQosData msgQosData = (MsgQosData)mu.getQosData().clone();
         msgQosData.setTopicProperty(null);
         if (entry instanceof MsgQueueUpdateEntry) {
            MsgQueueUpdateEntry updateEntry = (MsgQueueUpdateEntry)entry;
            msgQosData.setState(updateEntry.getState());
            msgQosData.setSubscriptionId(updateEntry.getSubscriptionId());
         }
         msgQosData.setQueueIndex(i);
         msgQosData.setQueueSize(entries.length);
         if (msgQosData.getNumRouteNodes() == 1) {
            msgQosData.clearRoutes();
         }
        
         ret.add(new MsgUnit(mu, null, null, msgQosData));
         // ret[i] = new MsgUnitRaw(mu, mu.getKeyData().toXml(), mu.getContent(), mu.getQosData().toXml());
      }
      if (consumable) queue.removeRandom(entries);
      return (MsgUnit[])ret.toArray(new MsgUnit[ret.size()]);
   }

   /**
    * We register for queue size changes and our blocking thread returns if we are done. 
    * Enforced by I_StorageSizeListener
    */
   public void changed(I_Storage storage, long numEntries, long numBytes, boolean isShutdown) {
      if (log.isLoggable(Level.FINER)) log.finer("changed numEntries='" + numEntries + "' numBytes='" + numBytes + "'");
      WaitingQuery[] queries = getWaitingQueries();
      for (int i=0; i<queries.length; i++) {
         if (isShutdown || !checkIfNeedsWaiting((int)numEntries, numBytes, queries[i])) {
            if (log.isLoggable(Level.FINE)) log.fine("changed going to notify");
            queries[i].startSignal.countDown();
         }
      }
   }
  
}
TOP

Related Classes of org.xmlBlaster.engine.query.plugins.QueueQueryPlugin$WaitingQuery

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.