Package org.xmlBlaster.util.queue.cache

Source Code of org.xmlBlaster.util.queue.cache.CacheQueueInterceptorPlugin

/*------------------------------------------------------------------------------
Name:      CacheQueueInterceptorPlugin.java
Project:   xmlBlaster.org
Copyright: xmlBlaster.org, see xmlBlaster-LICENSE file
------------------------------------------------------------------------------*/
package org.xmlBlaster.util.queue.cache;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.xmlBlaster.util.Global;
import org.xmlBlaster.util.XmlBlasterException;
import org.xmlBlaster.util.admin.I_AdminQueue;
import org.xmlBlaster.util.context.ContextNode;
import org.xmlBlaster.util.def.Constants;
import org.xmlBlaster.util.def.ErrorCode;
import org.xmlBlaster.util.plugin.PluginInfo;
import org.xmlBlaster.util.qos.storage.QueuePropertyBase;
import org.xmlBlaster.util.queue.I_Entry;
import org.xmlBlaster.util.queue.I_EntryFilter;
import org.xmlBlaster.util.queue.I_Queue;
import org.xmlBlaster.util.queue.I_QueueEntry;
import org.xmlBlaster.util.queue.I_QueuePutListener;
import org.xmlBlaster.util.queue.I_Storage;
import org.xmlBlaster.util.queue.I_StoragePlugin;
import org.xmlBlaster.util.queue.I_StorageProblemListener;
import org.xmlBlaster.util.queue.I_StorageSizeListener;
import org.xmlBlaster.util.queue.QueuePluginManager;
import org.xmlBlaster.util.queue.StorageId;
import org.xmlBlaster.util.queue.StorageSizeListenerHelper;
import org.xmlBlaster.util.queue.ram.RamQueuePlugin;

/**
* Implements a queue cache.
* Internally it utilizes a RAM queue and a JDBC queue and manages the cache logic.
* @see <a href="http://www.xmlblaster.org/xmlBlaster/doc/requirements/queue.cache.html">The queue.cache requirement</a>
* @author michele@laghi.eu
* @author xmlBlaster@marcelruff.info
*/
public class CacheQueueInterceptorPlugin implements I_Queue, I_StoragePlugin, I_StorageProblemListener, CacheQueueInterceptorPluginMBean
{
   private static Logger log = Logger.getLogger(CacheQueueInterceptorPlugin.class.getName());
   private String ME;
   private ContextNode contextNode;
   private QueuePropertyBase property;             // plugins via I_Queue
   private boolean notifiedAboutAddOrRemove = false;
   boolean isDown = true;
   private StorageId queueId;
   private I_QueuePutListener putListener;
//   private java.util.Properties pluginProperties;  // plugins via I_Plugin

   private I_Queue transientQueue;
   private I_Queue persistentQueue;
   private Global glob;
   private boolean isConnected = false;

   /** object used to control the swapping performance */
   //private CacheControlParam controlParam;
   private PluginInfo pluginInfo;

   /** this is the sync between the peaks and the swapping: no peak should be allowed while swapping */
   private Object peekSync = new Object();

   /** My JMX registration */
   private Object mbeanHandle;
  
   private long maxFetchSize = Long.MAX_VALUE;

   private StorageSizeListenerHelper storageSizeListenerHelper;
  
   public CacheQueueInterceptorPlugin() {
      this.storageSizeListenerHelper = new StorageSizeListenerHelper(this);
   }
  
   public boolean isTransient() {
      return this.transientQueue.isTransient() && this.persistentQueue.isTransient();
   }

   /**
    * Helper method to check the space left on a given queue.
    * @param  queue the queue on which to calculate the space left.
    * @param  valueToCheckAgainst the amount of bytes which are subtracted (needed in the queue) in this
              check.
    * @param  ifFullThrowException if 'true' this method will throw an exception if the return value would
              be negative
    * @return long the space left on the specified queue after having occupied the queue with what is
    *         specified in 'valueToCheckAgainst'
    * @throws XmlBlasterException if the 'ifFullThrowException' flag has been set to 'true' and the
    *         return value would be negative.
    */
   private final long checkSpaceAvailable(I_Queue queue, long valueToCheckAgainst, boolean ifFullThrowException, String extraTxt)
      throws XmlBlasterException {
      long spaceLeft = queue.getMaxNumOfBytes() - queue.getNumOfBytes() - valueToCheckAgainst;
      if (log.isLoggable(Level.FINE)) log.fine(ME+"maxNumOfBytes=" + queue.getMaxNumOfBytes() + "' numOfBytes='" + queue.getNumOfBytes() + "'. Occured at " + extraTxt);
      if (spaceLeft < 0L && (log.isLoggable(Level.FINE) || ifFullThrowException)) {
         String maxBytes = "maxBytes";
         String queueName = "Cache";
         if (queue == this.transientQueue) {
            maxBytes = "maxBytesCache";
            queueName = "Transient";
         }
         else if (queue == this.persistentQueue) {
            queueName = "Persistent";
         }
         String reason = queueName + " queue overflow, " + queue.getNumOfBytes() +
                         " bytes are in queue, try increasing '" +
                         this.property.getPropName(maxBytes) + "' on client login: " + extraTxt;
         if (log.isLoggable(Level.FINE)) log.fine(ME+ reason + this.toXml(""));
         if (ifFullThrowException)
            throw new XmlBlasterException(glob, ErrorCode.RESOURCE_OVERFLOW_QUEUE_BYTES, ME, reason);
      }
      return spaceLeft;
   }

   private final long checkEntriesAvailable(I_Queue queue, long valueToCheckAgainst, boolean ifFullThrowException, String extraTxt)
      throws XmlBlasterException {
      long entriesLeft = queue.getMaxNumOfEntries() - queue.getNumOfEntries() - valueToCheckAgainst;
      if (entriesLeft < 0L && (log.isLoggable(Level.FINE) || ifFullThrowException)) {
         String maxEntries = "maxEntries";
         String queueName = "Cache";
         if (queue == this.transientQueue) {
            maxEntries = "maxEntriesCache";
            queueName = "Transient";
         }
         else if (queue == this.persistentQueue) {
            queueName = "Persistent";
         }
         String reason = queueName + " queue overflow, " + queue.getNumOfEntries() +
                         " entries are in queue, try increasing '" +
                         this.property.getPropName(maxEntries) + "' on client login: " + extraTxt;
         if (log.isLoggable(Level.FINE)) log.fine(ME+ reason + this.toXml(""));
         if (ifFullThrowException)
            throw new XmlBlasterException(glob, ErrorCode.RESOURCE_OVERFLOW_QUEUE_ENTRIES, ME, reason);
      }
      return entriesLeft;
   }


   /**
    * @see I_StorageProblemListener#storageUnavailable(int)
    */
   synchronized public void storageUnavailable(int oldStatus) {
      if (log.isLoggable(Level.FINER))
         log.finer(ME+"entering storageUnavailable");
      this.isConnected = false;
      // we could optimize this by providing a peekLast method to the I_Queue
      try {
         this.transientQueue.peek(-1, -1L);
      }
      catch (XmlBlasterException ex) {
         log.severe(ME+"storageUnavailable: exception occured when peeking the transient queue: " + ex.getMessage());
         ex.printStackTrace();
      }
   }


   /**
    * @see I_StorageProblemListener#storageAvailable(int)
    */
   synchronized public void storageAvailable(int oldStatus) {
      if (oldStatus == I_StorageProblemListener.UNDEF) return;
      if (log.isLoggable(Level.FINER)) log.finer(ME+"entering storageAvailable");
     /* remove all obsolete messages from the persitence. */

      if (this.persistentQueue == null) return; // should never happen

      try {
         boolean isInclusive = true; // if the reference is the original one then it is inclusive, if it is a new one then it is exclusive
         I_QueueEntry limitEntry = null; // this.referenceEntry;
         if (log.isLoggable(Level.FINE)) {
            if (limitEntry == null) log.fine(ME+"The reference entry is null");
            else log.fine(ME+"The reference entry is '" + limitEntry.getUniqueId() + "' and its flag 'stored' is '" + limitEntry.isStored() + "'");
         }
         List<I_Entry> list = null;

         if (limitEntry == null || limitEntry.isStored()) {
            isInclusive = false;
            limitEntry = this.transientQueue.peek(); // get the first entry in the RAM queue as ref
            if (log.isLoggable(Level.FINE)) {
               if (limitEntry == null) log.fine(ME+"The new reference entry is null");
               else log.fine(ME+"The new reference entry is '" + limitEntry.getUniqueId() + "'");
            }
         }
         if (limitEntry == null) { // then ram queue was empty when it lost connection and is empty now
            isInclusive = false;
            this.persistentQueue.clear();
         }


         // remove all old msg which are higher than the reference entry all more important msg were sent already        
         else this.persistentQueue.removeWithLimitEntry(limitEntry, isInclusive);

         limitEntry = this.persistentQueue.peek();
         if (limitEntry != null) {
            list = this.transientQueue.peekWithLimitEntry(limitEntry);
            if (list.size() > 0) {
               // TAKE AWAY ALL TRANSIENTS !!!!!!
               long countToPut = this.persistentQueue.getMaxNumOfEntries() - this.persistentQueue.getNumOfEntries();
               long bytesToPut = this.persistentQueue.getMaxNumOfBytes() - this.persistentQueue.getNumOfBytes();
               long currBytes = 0L;
               ArrayList list2 = new ArrayList();
               for (int i=list.size()-1; i >= 0; i--) {
                  I_Entry entry = (I_Entry)list.get(i);
                  if (entry.isPersistent()) {
                     if (currBytes >= bytesToPut || list2.size() >= countToPut) {
                        break;
                     }
                     list2.add(entry);
                     currBytes += entry.getSizeInBytes();
                  }
               }
               if (list2.size() > 0) {
                  this.persistentQueue.put((I_QueueEntry[])list2.toArray(new I_QueueEntry[list2.size()]), false);
               }
           }
         }
         this.isConnected = true;
      }
      catch (XmlBlasterException ex) {
         log.severe(ME+"exception occured when reconnecting. " + ex.getMessage());
         ex.printStackTrace();
      }
      finally {
         try {
            loadFromPersistence();
         }
         catch (XmlBlasterException ex) {
            log.severe(ME+"storageAvailable: exception when loading from persistence: " + ex.getMessage());
            ex.printStackTrace();
         }
      }
   }


   /**
    * Is called after the instance is created.
    * @param uniqueQueueId A unique name, allowing to create a unique name for a persistent store (e.g. file name)
    * @see I_Queue#initialize(StorageId, Object)
    */
   synchronized public void initialize(StorageId uniqueQueueId, Object userData)
      throws XmlBlasterException
   {
      if (this.isDown) {

         java.util.Properties pluginProperties = null;
         if (this.pluginInfo != null) pluginProperties = this.pluginInfo.getParameters();

         if (pluginProperties == null)
            pluginProperties = new java.util.Properties(); // if loaded from testsuite without a PluginManager

         this.property = null;
         this.glob = ((QueuePropertyBase)userData).getGlobal();

         this.ME = uniqueQueueId.toString() + ": ";
         if (log.isLoggable(Level.FINER)) log.finer(ME+"initialized");
         this.queueId = uniqueQueueId;

         // For JMX instanceName may not contain ","
         String instanceName = this.glob.validateJmxValue(this.queueId.getId());
         this.contextNode = new ContextNode(ContextNode.QUEUE_MARKER_TAG, instanceName,
                             this.glob.getContextNode()); // TODO: pass from real parent like SubjectInfo
         this.mbeanHandle = this.glob.registerMBean(this.contextNode, this);

         QueuePluginManager pluginManager = glob.getQueuePluginManager();
         QueuePropertyBase queuePropertyBase = (QueuePropertyBase)userData;

         try {
           this.maxFetchSize = Long.valueOf(pluginProperties.getProperty("maxFetchSize", ""+maxFetchSize)).longValue();
         }
         catch (Throwable e) {
             log.warning(ME+"Setting maxFetchSize failed: " + e.toString());
         }

         //instantiate and initialize the underlying queues
         String defaultTransient = pluginProperties.getProperty("transientQueue", "RAM,1.0").trim();
         if (defaultTransient.startsWith(getType())) {
            log.severe(ME+"Cache queue configured with transientQueue=CACHE, to prevent recursion we set it to 'RAM,1.0'");
            defaultTransient = "RAM,1.0";
         }
         QueuePropertyBase ramProps = createRamCopy(queuePropertyBase);
         ramProps.setEmbedded(true);
         this.transientQueue = pluginManager.getPlugin(defaultTransient, uniqueQueueId, ramProps);
         //log.error(ME, "Debug only: " + this.transientQueue.toXml(""));
        
         try {
            String defaultPersistent = pluginProperties.getProperty("persistentQueue", "JDBC,1.0").trim();
            if (defaultPersistent.startsWith(getType())) {
               log.severe(ME+"Cache queue configured with persistentQueue=CACHE, to prevent recursion we set it to 'JDBC,1.0'");
               defaultPersistent = "JDBC,1.0";
            }
            boolean oldEmbedded = queuePropertyBase.isEmbedded(); // since a CACHE could be inside a CACHE
            queuePropertyBase.setEmbedded(true);
            this.persistentQueue = pluginManager.getPlugin(defaultPersistent, uniqueQueueId, queuePropertyBase);
            queuePropertyBase.setEmbedded(oldEmbedded); // since it is not a clone we make sure to reset it to its original

            this.isConnected = true;
            // to be notified about reconnections / disconnections
//            this.glob.getJdbcQueueManager(this.queueId).registerListener(this);
            this.persistentQueue.registerStorageProblemListener(this);
         }
         catch (XmlBlasterException ex) {
            log.severe(ME+"Could not initialize the persistent queue '" + uniqueQueueId + "'. Is the JDBC Driver jar file in the CLASSPATH ?" +
                " Is the DB up and running ? We continue RAM based ..." + ex.getMessage() +
                " The propery settings are:" + queuePropertyBase.toXml());
            // start a polling thread to see if the connection can be established later
            ex.printStackTrace();
         }

         // do the queue specific stuff like delete all volatile entries in
         // the persistent queue
         if (isPersistenceAvailable()) {
            /*
            // not so performant but only called on init
            //this.numOfBytes = -999L;
            this.numOfBytes = getNumOfBytes_();
            try {
               this.persistentQueue.removeTransient();
            }
            catch (XmlBlasterException ex) {
               log.severe(ME+"could not remove transient entries (swapped entries) probably due to no connection to the DB, or the DB is down");
               ex.printStackTrace();
            }
            */

            setProperties(userData);
            // not used yet
            //this.controlParam = new CacheControlParam((QueuePropertyBase)getProperties());

            loadFromPersistence();

            // on restart the added() event is not triggered!

         } // persistentQueue!=null
         this.isDown = false;
         if (log.isLoggable(Level.FINE)) log.fine(ME+"Successful initialized");
      } // isDown?
   }

   /**
    * We set the cache props to the real props for RAM queue running under a cacheQueue
    */
   private QueuePropertyBase createRamCopy(QueuePropertyBase queuePropertyBase) {
      QueuePropertyBase ramCopy = (QueuePropertyBase)queuePropertyBase.clone();
      ramCopy.setMaxEntries(queuePropertyBase.getMaxEntriesCache());
      ramCopy.setMaxBytes(queuePropertyBase.getMaxBytesCache());
      return ramCopy;
   }

   /**
    * @see I_Queue#setProperties(Object)
    */
   synchronized public void setProperties(Object userData) throws XmlBlasterException {
      if (userData == null) return;
      QueuePropertyBase newProp;
      try {
         newProp = (QueuePropertyBase)userData;
      }
      catch(Throwable e) {
         log.severe(ME+"Can't configure queue, your properties are invalid: " + e.toString());
         e.printStackTrace();
         return;
      }

      /* Do we need to protect against shrinking?
      if (this.property != null && this.property.getMaxEntries() > newProp.getMaxEntries()) {
         log.warn(ME, "Reconfigure of a RamQueuePlugin - getMaxNumOfEntries from " + this.property.getMaxEntries() +
                    " to " + newProp.getMaxEntries() + " is not supported, we ignore the new setting.");
         return;
      }
      */

      this.property = newProp;
      this.transientQueue.setProperties(createRamCopy((QueuePropertyBase)userData));
      if (this.persistentQueue != null) this.persistentQueue.setProperties(userData);
   }

   // JMX
   public String getPropertyStr() {
      return (this.property == null) ? "" : this.property.toXml();
   }

   /**
    * Access the current queue configuration
    */
   public Object getProperties() {
      return this.property;
   }

   public void setNotifiedAboutAddOrRemove(boolean notify) {
      this.notifiedAboutAddOrRemove = notify;
   }

   public boolean isNotifiedAboutAddOrRemove() {
      return this.notifiedAboutAddOrRemove;
   }

   /**
    * @see I_Queue#addPutListener(I_QueuePutListener)
    */
   synchronized public void addPutListener(I_QueuePutListener l) {
      if (l == null)
         throw new IllegalArgumentException(ME + ": addPustListener(null) is not allowed");
      if (this.putListener != null)
         throw new IllegalArgumentException(ME + ": addPustListener() failed, there is a listener registered already");
      this.putListener = l;
   }

   /**
    * @see I_Queue#removePutListener(I_QueuePutListener)
    */
   synchronized public void removePutListener(I_QueuePutListener l) {
      this.putListener = null;
   }

   /**
    * returns the persistent queue (null if no one defined)
    */
   public I_Queue getPersistentQueue() {
      return this.persistentQueue;
   }

   /**
    * returns the transient queue (null if no one defined)
    */
   public I_Queue getTransientQueue() {
      return this.transientQueue;
   }


   /**
    * Gets the references of the entries in the queue. Note that the data
    * which is referenced here may be changed by other threads.
    */
   public long[] getEntryReferences() throws XmlBlasterException {
      // currently not implemented
      throw new XmlBlasterException(glob, ErrorCode.INTERNAL_NOTIMPLEMENTED, ME, "getEntryReferences not implemented");
   }

   /**
    * @see I_Queue#getEntries(I_EntryFilter)
    */
   public List<I_Entry> getEntries(I_EntryFilter entryFilter) throws XmlBlasterException {
           if (this.persistentQueue == null) return new ArrayList();
           return this.persistentQueue.getEntries(entryFilter);
   }

   /**
    * @see I_Queue#put(I_QueueEntry, boolean)
    */
   public void put(I_QueueEntry queueEntry, boolean ignorePutInterceptor)
      throws XmlBlasterException {
      I_QueueEntry[] entries = new I_QueueEntry[1];
      entries[0] = queueEntry;
      put(entries, ignorePutInterceptor);
   }


   /**
    * All entries are stored into the transient queue. All persistent messages are
    * stored also in the persistent queue. The exceeding size in the transient
    * queue is calculated. If it is positive it means we need to swap. The
    * overflowing messages are taken from the ram queue. The volatile between
    * them are stored in the persistent storage (since the persistent ones have
    * been previously stored).
    *
    * @see I_Queue#put(I_QueueEntry[], boolean)
    */
   public void put(I_QueueEntry[] queueEntries, boolean ignorePutInterceptor)
      throws XmlBlasterException {
      if (queueEntries == null || queueEntries.length < 1) return;

      if ((this.putListener != null) && (!ignorePutInterceptor)) {
         // Is an interceptor registered (and not bypassed) ?
         if (this.putListener.putPre(queueEntries) == false)
            return; // supress adding entries to queue (bypasses the queue)
      }

      synchronized (this) {
         checkEntriesAvailable(this, 0L, true, "first check in put"); // throws XmlBlasterException if no space left
         checkSpaceAvailable(this, 0L, true,  "first check in put"); // throws XmlBlasterException if no space left
       
         long sizeOfEntries = 0L;
         // separate persistent from transient messages and store the persistents in persistence
         if (isPersistenceAvailable()) {
            ArrayList persistentsFromEntries = new ArrayList();
            long sizeOfPersistents = 0L;
            long numOfPersistents = 0L;
       
            for (int i=0; i < queueEntries.length; i++) {
               if (queueEntries[i].isPersistent()) {
                  persistentsFromEntries.add(queueEntries[i]);
                  sizeOfPersistents += queueEntries[i].getSizeInBytes();
                  numOfPersistents++;
               }
               else sizeOfEntries += queueEntries[i].getSizeInBytes();
            }
            sizeOfEntries += sizeOfPersistents;
       
            if (persistentsFromEntries.size() > 0) {
               try {
                  this.persistentQueue.put((I_QueueEntry[])persistentsFromEntries.toArray(new I_QueueEntry[persistentsFromEntries.size()]), ignorePutInterceptor);
               }
               catch (XmlBlasterException ex) {
                  log.severe(ME+"put: an error occured when writing to the persistent queue: " + persistentsFromEntries.size() + " persistent entries will temporarly be handled as transient. Is the DB up and running ? " + ex.getMessage() + "state "  + this.toXml(""));
                  // should an exception be rethrown here ? No because it should be possible to work even if no persistence available
               }
               catch (Throwable ex) {
                  log.severe(ME+"put: an error occured when writing to the persistent queue: " + persistentsFromEntries.size() + " persistent entries will temporarly be handled as transient. Is the DB up and running ? " + ex.toString() + "state "  + this.toXml(""));
                  ex.printStackTrace();
               }
            }
         }
       
       
         synchronized(this.peekSync) {       
            // put all messages on transient queue
            this.transientQueue.put(queueEntries, ignorePutInterceptor);

            if (isPersistenceAvailable()) { // if no persistence available let RAM overflow one time

               // handle swapping (if any)
               // TODO swap only if bigger than max entries (not same as max entries)
               long exceedingSize = -checkSpaceAvailable(this.transientQueue, 0L, false, "second check in put");
               long exceedingEntries = -checkEntriesAvailable(this.transientQueue, 0L, false, "second check in put");
               if ( (exceedingSize >= 0L && this.persistentQueue.getMaxNumOfBytes() > this.transientQueue.getMaxNumOfBytes()) ||
                    (exceedingEntries >= 0L && this.persistentQueue.getMaxNumOfEntries() > this.transientQueue.getMaxNumOfEntries())) {
                  if (log.isLoggable(Level.FINE)) log.fine(ME+"Swapping. Exceeding size (in bytes): " + exceedingSize + " exceeding entries: " + exceedingEntries + " state: " + toXml(""));
           
                  List<I_Entry> transients = null;
                  try {
                     List<I_Entry> swaps = null;
                     boolean needsLoading = false;
                     if (this.transientQueue.getNumOfEntries() == 0)
                        swaps = this.transientQueue.takeLowest((int)exceedingEntries, exceedingSize, null, true);
                     else {
                        swaps = this.transientQueue.takeLowest(queueEntries.length, sizeOfEntries, null, true);
                        needsLoading = true;
                     }
                     if (log.isLoggable(Level.FINE)) {
                        log.fine(ME+"Swapping: moving '" + swaps.size() + "' entries from transient queue to persistent queue: exceedingEntries='" + exceedingEntries + "' and exceedingSize='" + exceedingSize + "'");
                     }
                     // get the transients
                     transients = new ArrayList<I_Entry>();
                     for (int i=0; i < swaps.size(); i++) {
                        I_QueueEntry entry = (I_QueueEntry)swaps.get(i);
                        if (!entry.isPersistent()) {
                           transients.add(entry);
                        }
                     }
                     if (transients.size() > 0)
                        this.persistentQueue.put((I_QueueEntry[])transients.toArray(new I_QueueEntry[transients.size()]), ignorePutInterceptor);
                     if (needsLoading) loadFromPersistence();
                  }
                  catch (XmlBlasterException ex) {
                     log.severe(ME+"put: an error occured when swapping: " +  transients.size() + ". Is the DB up and running ? " + ex.getMessage() + " state: " + toXml(""));
                     ex.printStackTrace();
                  }
                  catch (Throwable ex) {
                     log.severe(ME+"put: an error occured when swapping: " +  transients.size() + ". Is the DB up and running ? " + ex.toString());
                     ex.printStackTrace();
                  }
               }
            }
         } // end of peekSync here ...
      } // end of synchronized here ...

      // these must be outside the synchronized ...
      if (this.notifiedAboutAddOrRemove) {
         for(int i=0; i<queueEntries.length; i++)
            try {
               queueEntries[i].added(this.queueId);
            }
            catch (Throwable ex) {
               log.severe(ME+"put: an error occured when notifying : " + ex.toString());
               ex.printStackTrace();
            }
      }
      this.storageSizeListenerHelper.invokeStorageSizeListener();
      if ((this.putListener != null) && (!ignorePutInterceptor)) {
         this.putListener.putPost(queueEntries);
      }
   }

   // JMX
   public String getQueueName() {
      return getStorageId().getStrippedId();
   }

   /**
    * Returns the unique ID of this queue
    */
   public StorageId getStorageId() {
      return this.queueId;
   }

   /**
    * @see I_Queue#takeWithPriority(int,long,int,int)
    */
   public List<I_Entry> takeWithPriority(int numOfEntries, long numOfBytes, int minPriority, int maxPriority)
      throws XmlBlasterException {
      throw new XmlBlasterException(glob, ErrorCode.INTERNAL_NOTIMPLEMENTED, ME, "takeWithPriority not implemented");
      // if (this.notifiedAboutAddOrRemove) {}
   }


   private final boolean hasTransientsSwapped() {
      return this.persistentQueue.getNumOfPersistentEntries() != this.persistentQueue.getNumOfEntries();
   }

   private final boolean isPersistenceAvailable() {
      return this.persistentQueue != null && this.isConnected;
   }
     
   private final boolean hasUncachedEntries() {
      return hasTransientsSwapped() ||
             this.persistentQueue.getNumOfPersistentEntries() != this.transientQueue.getNumOfPersistentEntries();
   }


   /**
    * Aware: peekLowest is not implemented!!
    * @see I_Queue#peekLowest(int, long, I_QueueEntry, boolean)
    */
   public List<I_Entry> peekLowest(int numOfEntries, long numOfBytes, I_QueueEntry limitEntry, boolean leaveOne)
      throws XmlBlasterException {
      throw new XmlBlasterException(glob, ErrorCode.INTERNAL_NOTIMPLEMENTED, ME, "peekLowest is not implemented");
   }


   /**
    * Aware: takeLowest for more than one entry is not implemented!!
    * @see I_Queue#takeLowest(int, long, I_QueueEntry, boolean)
    */
   public List<I_Entry> takeLowest(int numOfEntries, long numOfBytes, I_QueueEntry limitEntry, boolean leaveOne)
      throws XmlBlasterException {
      List<I_Entry> list = null;
      boolean doNotify = false;
      try {
         synchronized(this) {
            boolean handlePersistents = isPersistenceAvailable() && hasUncachedEntries();
            if ( handlePersistents ) {
               // swapping
               try {
                  list = this.persistentQueue.takeLowest(numOfEntries, numOfBytes, limitEntry, leaveOne);
                  doNotify = true;
               }
               catch (Throwable ex) {
                  handlePersistents = false;
                  log.severe(ME+"takeLowest: exception occured when taking the lowest entry from the persistent queue: " + ex.toString());
                  ex.printStackTrace();
               }
          
               if (handlePersistents) {
                  if (list.size() > 1) {
                     throw new XmlBlasterException(glob, ErrorCode.INTERNAL_NOTIMPLEMENTED, ME,
                              "takeLowest for more than one entry is not implemented");
                  }
          
                  long num = 0L;
                  boolean[] hlp = this.transientQueue.removeRandom((I_Entry[])list.toArray(new I_Entry[list.size()]));
                  for (int i=0; i < hlp.length; i++) if (hlp[i]) num++;
                  if (num > 0L) {
                     if (log.isLoggable(Level.FINE)) log.fine(ME+"Didn't expect message " + ((I_Entry)list.get(0)).getLogId() + " in transient store." +
                                " If the database was temporary unavailable this is possible " + this.toXml(""));
                  }
               }
            }
            // 'else' is no good here since it could have changed due to the exception ...
            if ( !handlePersistents) {
               list = this.transientQueue.takeLowest(numOfEntries, numOfBytes, limitEntry, leaveOne);
               doNotify = true;
               if (isPersistenceAvailable() && list.size() > 0 && this.persistentQueue.getNumOfEntries() > 0) {
                  boolean durableFound = false;
                  for(int i=0; i<list.size(); i++) {
                     if (((I_Entry)list.get(i)).isPersistent()) {
                        durableFound = true;
                        break;
                     }
                  }
                  if (durableFound) {
                     this.persistentQueue.removeRandom((I_Entry[])list.toArray(new I_Entry[list.size()]));
                  }
               }
            }
       
         } // end of syncrhonized
      }
      finally {
         if (doNotify) {
            if (this.notifiedAboutAddOrRemove) {
               for(int i=0; i<list.size(); i++) ((I_Entry)list.get(i)).removed(this.queueId);
            }
         }
         this.storageSizeListenerHelper.invokeStorageSizeListener();
      }
      return list;
   }

   // JMX
   public String peekStr() throws Exception {
      try {
         I_QueueEntry entry = peek();
         return (entry == null) ? "No entry found" :
                     entry.getLogId() + " - " + entry.getSizeInBytes() + " bytes - " +
                     ((entry.isPersistent()) ? "persistent" : "transient") +
                     " prio=" + entry.getPriority() +
                     " " + entry.getEmbeddedType(); // no toXml() available ??
      }
      catch(XmlBlasterException e) {
         throw new Exception(e);
      }
   }

   // JMX
   public String[] peekEntries(int numOfEntries) throws Exception {
      if (numOfEntries == 0)
         return new String[] { "Please pass number of messages to peak" };
      try {
         List<I_Entry> list = peek(numOfEntries, -1L);
         if (list == null || list.size()<1)
            return new String[] { "No entry found" };
         String[] ret = new String[list.size()];
         for (int i=0; i<list.size(); i++) {
            I_QueueEntry entry = (I_QueueEntry)list.get(i);
            // no toXml() available ??
            ret[i] = entry.getLogId() + " - " + entry.getSizeInBytes() + " bytes - " +
                     ((entry.isPersistent()) ? "persistent" : "transient") +
                     " prio=" + entry.getPriority() +
                     " " + entry.getEmbeddedType(); // no toXml() available ??
         }
         return ret;
      }
      catch (XmlBlasterException e) {
         throw new Exception(e);
      }
   }

   /**
    * @see I_Queue#peek()
    */
   public I_QueueEntry peek() throws XmlBlasterException {
      synchronized(this.peekSync) {
         return this.transientQueue.peek();
      }
   }

   /**
    * @see I_Queue#peek(int,long)
    */
   public List<I_Entry> peek(int numOfEntries, long numOfBytes) throws XmlBlasterException {
      synchronized(this.peekSync) {
         return this.transientQueue.peek(numOfEntries, numOfBytes);
      }
   }

   public List<I_Entry> peekStartAt(int numOfEntries, long numOfBytes, I_QueueEntry firstEntryExlusive)
         throws XmlBlasterException {
      throw new IllegalAccessError("CacheQueueInterceptorPlugin has peekStartAt not implemented");
   }

   /**
    * @see I_Queue#peekSamePriority(int, long)
    */
   public List<I_Entry> peekSamePriority(int numOfEntries, long numOfBytes) throws XmlBlasterException {
      synchronized(this.peekSync) {
         return this.transientQueue.peekSamePriority(numOfEntries, numOfBytes);
      }
   }

   /**
    * @see I_Queue#peekWithPriority(int, long, int, int)
    */
   public List<I_Entry> peekWithPriority(int numOfEntries, long numOfBytes, int minPriority, int maxPriority)
         throws XmlBlasterException {
      synchronized(this.peekSync) {
         return this.transientQueue.peekWithPriority(numOfEntries, numOfBytes, minPriority, maxPriority);
      }
   }

   /**
    * @see I_Queue#peekWithLimitEntry(I_QueueEntry)
    * @deprecated
    */
   public List<I_Entry> peekWithLimitEntry(I_QueueEntry limitEntry) throws XmlBlasterException {
      synchronized(this.peekSync) {
         return this.transientQueue.peekWithLimitEntry(limitEntry);
      }
   }


   /**
    * @see I_Queue#removeWithLimitEntry(I_QueueEntry, boolean)
    */
   synchronized public long removeWithLimitEntry(I_QueueEntry limitEntry, boolean inclusive) throws XmlBlasterException {
      long ret = this.transientQueue.removeWithLimitEntry(limitEntry, inclusive);

      if (isPersistenceAvailable()) {
         try {
            ret = this.persistentQueue.removeWithLimitEntry(limitEntry, inclusive);
         }
         catch (XmlBlasterException ex) {
            log.severe(ME+"removeWithLimitEntry: exception occured when removing from persistence. reason: " + ex.getMessage());
            ex.printStackTrace();
         }
         catch (Throwable ex) {
            log.severe(ME+"removeWithLimitEntry: exception occured when removing from persistence. reason: " + ex.toString());
            ex.printStackTrace();
         }
      }
      return ret;
   }


   /**
    * Removes the first element in the queue
    * This method does not block.
    * @return Number of messages erased (0 or 1)
    * @throws XmlBlasterException if the underlying implementation gets an exception.
    */
   public int remove() throws XmlBlasterException {
      return (int)removeNum(1);
   }


   /**
    * Removes max num messages.
    * This method does not block.
    * @param num Erase num entries or less if less entries are available, -1 erases everything
    * @return Number of entries erased
    * @throws XmlBlasterException if the underlying implementation gets an exception.
    */
   public long removeNum(long numOfEntries) throws XmlBlasterException {
     
      long ret = 0L;
      int removedEntries = 0;
      if (numOfEntries > Integer.MAX_VALUE)
         throw new XmlBlasterException(glob, ErrorCode.INTERNAL_ILLEGALARGUMENT, ME, "remove: too many entries to remove " + numOfEntries);
      int nmax = (int)numOfEntries;

      if (nmax < 0) nmax = Integer.MAX_VALUE;

      I_Entry[] entries = null;
      List<I_Entry> list = null;
      boolean[] tmp = null;
      try {
         synchronized(this) {
            while ((nmax > 0)) {
               list = peek(nmax, -1L);
               if ((list == null) || (list.size() < 1)) break;
               removedEntries = 0;
               entries = (I_Entry[])list.toArray(new I_Entry[list.size()]);
               tmp = removeRandomNoNotify(entries);
               for (int i=0; i < tmp.length; i++)
                  if (tmp[i]) removedEntries++;
               nmax -= removedEntries;
               ret += removedEntries;
            }
         }
      }
      finally {
         if (this.notifiedAboutAddOrRemove && tmp!=null && entries!=null) {
            for(int i=0; i<tmp.length; i++)
               if (tmp[i])
                  entries[i].removed(this.queueId);
         }
      }
      this.storageSizeListenerHelper.invokeStorageSizeListener();
      return ret;
   }


   /**
    * Removes the given entry.
    * @param dataId the unique id. It must be unique within the storage area
    *        of the implementing queue. In other words, if the underlying
    *        implementation is on RAM, then the storage area is the JVM, that
    *        is the queue must be unique in the same JVM. If the queue is a
    *        jdbc, the dataId is unique in the DB used.
    */
   public int removeRandom(long dataId) throws XmlBlasterException {
      throw new XmlBlasterException(glob, ErrorCode.INTERNAL_NOTIMPLEMENTED, ME, "removeRandom(long) not implemented");
   }


   /**
    * Removes the given entries.
    * @param msgQueueEntry the entry to erase.
    */
   public long removeRandom(long[] dataIdArray) throws XmlBlasterException {
      throw new XmlBlasterException(glob, ErrorCode.INTERNAL_NOTIMPLEMENTED, ME, "removeRandom(long[]) not implemented");
   }


   /**
    * @see I_Queue#removeRandom(I_Entry)
    */
   public int removeRandom(I_Entry entry) throws XmlBlasterException {
      I_Entry[] entries = new I_Entry[1];
      entries[0] = entry;
      if (removeRandom(entries)[0]) return 1;
      else return 0;
   }

   /**
    * The given ret array will be updated with the result of the removing from the persistent
    * queue.
    */
   private final boolean[] removePossibleSwappedEntries(boolean[] ret, I_Entry[] queueEntries) {
      if (log.isLoggable(Level.FINER)) log.finer(ME+"removePossibleSwappedEntries");

      // prepare the entries array
      if (!isPersistenceAvailable()) return ret;
      int numUnremoved = 0;
      for (int i=0; i < ret.length; i++) if (!ret[i]) numUnremoved++;
      if (numUnremoved == 0) return ret;
      if (!hasTransientsSwapped()) return ret;

      if (log.isLoggable(Level.FINE)) log.fine(ME+"There were entries '" + numUnremoved + "' to delete on persistence");
      if (queueEntries == null || queueEntries.length < 1) return ret;
     
      I_Entry[] unremovedEntries = new I_Entry[numUnremoved];
      int count = 0;
      for (int i=0; i < ret.length; i++) {
         if (!ret[i]) {
            unremovedEntries[count] = queueEntries[i];
            count++;
         }
      }

      try {
         boolean[] ret1 = this.persistentQueue.removeRandom(unremovedEntries);
         count = 0;
         for (int i=0; i < ret.length; i++) {
            if (!ret[i]) {
               ret[i] = ret1[count];
               count++;
               if (log.isLoggable(Level.FINEST)) log.finest(ME+"Entry '" + unremovedEntries[count].getUniqueId() + "' has been deleted ? : " + ret1[count]);
            }
         }
      }
      catch (XmlBlasterException ex) {
         log.severe(ME+"exception occured when trying to remove entries which have supposely been swapped since the last peek. reason: " + ex.getMessage());
         ex.printStackTrace();
         return ret;
      }
      return ret;
   }

   /**
    * @see I_Queue#removeRandom(I_Entry[])
    */
   private final boolean[] removeRandomNoNotify(I_Entry[] queueEntries) throws XmlBlasterException {
      if (log.isLoggable(Level.FINER)) log.finer(ME+"removeRandom(I_QueueEntry[])");
      if (queueEntries == null || queueEntries.length < 1) return new boolean[0];
      boolean[] ret = null;

      synchronized (this) {
         try {
           if (isPersistenceAvailable()) {
               List<I_Entry> persistents = new ArrayList<I_Entry>();
               for (int i=0; i < queueEntries.length; i++) {
                  if (queueEntries[i].isPersistent()) persistents.add(queueEntries[i]);
               }
               if (log.isLoggable(Level.FINE)) log.fine(ME+"Remove " + persistents.size() + " persistent entries from persistent storage");
               try {
                  this.persistentQueue.removeRandom((I_Entry[])persistents.toArray(new I_Entry[persistents.size()]));
               }
               catch (XmlBlasterException ex) {
                  log.severe(ME+"could not remove " + persistents.size() + " entries from the persistent queue. Probably due to failed connection to the DB exception: " +  ex.getMessage());
                  ex.printStackTrace();
               }
            }
          
            // and now the transient queue (the ram queue)
            if (log.isLoggable(Level.FINE)) log.fine(ME+"Removing from transient queue " + queueEntries.length + " entries");
            try {
               ret = this.transientQueue.removeRandom(queueEntries);
               ret = removePossibleSwappedEntries(ret, queueEntries);
            }
            catch (XmlBlasterException ex) {
               log.severe(ME+"could not remove " + queueEntries.length + " entries from the transient queue.: " + ex.getMessage());
               ex.printStackTrace();
            }
         }
         finally {
            try {
               loadFromPersistence();
            }
            catch (XmlBlasterException ex1) {
               log.severe(ME+"removeRandom exception occured when loading from persistence: " + ex1.getMessage());
               ex1.printStackTrace();
            }
         }
      }
      return ret;
   }


   /**
    * @see I_Queue#removeRandom(I_Entry[])
    */
   public final boolean[] removeRandom(I_Entry[] queueEntries) throws XmlBlasterException {
      boolean[] ret = removeRandomNoNotify(queueEntries);
      if (this.notifiedAboutAddOrRemove) {
         for(int i=0; i<ret.length; i++) {
            if (ret[i]) {
              try {
                 queueEntries[i].removed(this.queueId);
              }
              catch (Throwable ex) {
                 log.severe(ME+"removeRandom: exception when notifying about removal: " + ex.toString());
                 ex.printStackTrace();
              }
            }
         }
      }
      this.storageSizeListenerHelper.invokeStorageSizeListener();
      return ret;
   }


   /**
    * Loads from the persistence so much data as it fits into the transient
    * queue.
    */
   synchronized private final int loadFromPersistence() throws XmlBlasterException {
      if (!isPersistenceAvailable()) return 0;

      // load further entries from persistence into transient queue
      if(hasUncachedEntries()) {
         //or should it only fill a certain amount (percent) of the queue size ?
         long freeEntries = this.transientQueue.getMaxNumOfEntries() - this.transientQueue.getNumOfEntries();
         if (freeEntries > maxFetchSize) freeEntries = maxFetchSize;
         long freeBytes = this.transientQueue.getMaxNumOfBytes() - this.transientQueue.getNumOfBytes();

         if (freeEntries <= 0L || freeBytes <= 0L) {
            if (log.isLoggable(Level.FINE))
               log.fine(ME+"The transient queue is already full." +
                          " numOfBytes=" + this.transientQueue.getNumOfBytes() +
                          " maxNumOfBytes=" + this.transientQueue.getMaxNumOfBytes() +
                          " numOfEntries=" + this.transientQueue.getNumOfEntries() +
                          " maxNumOfEntries=" + this.transientQueue.getMaxNumOfBytes());
            if (log.isLoggable(Level.FINEST)) log.finest(ME+"The real current size in bytes of transient queue is: " + ((RamQueuePlugin)this.transientQueue).getSynchronizedNumOfBytes());
            return 0;
         }
         if (log.isLoggable(Level.FINE)) log.fine(ME+"Swapping: reloading from persistence for a length of " + freeBytes);

         // 1. Look into persistent store ...
         List<I_Entry> list = null;
         try {
            List<I_Entry> listLowest = this.transientQueue.peekLowest(1, -1, null, false);
            I_QueueEntry firstEntry = null;
            if (listLowest.size() == 1) {
               firstEntry = (I_QueueEntry)listLowest.get(0);
            }
            list = this.persistentQueue.peekStartAt((int)freeEntries, freeBytes, firstEntry);
         }
         catch (XmlBlasterException ex) {
            log.severe(ME+"Could not read back data from persistence: " + ex.getMessage());
            ex.printStackTrace();
         }

         if (list == null || list.size() < 1) {
            return 0;
         }

         // 2. Put it into RAM ...
         try {
            this.transientQueue.put((I_QueueEntry[])list.toArray(new I_QueueEntry[list.size()]), false);
         }
         catch (XmlBlasterException ex) {
            log.severe(ME+"loadFromPeristence: no space left on transient queue: " + ex.getMessage());
            ex.printStackTrace();
            return 0;
         }

         // 3. Erase the swapped and transient entries from persistence ...
         List<I_Entry> transients = new ArrayList<I_Entry>();
         int n = list.size();
         for(int i=0; i<n; i++) {
            if (!((I_Entry)list.get(i)).isPersistent()) {
               transients.add(list.get(i));
            }
         }
         try {
            if (transients.size() > 0)
               this.persistentQueue.removeRandom((I_Entry[])transients.toArray(new I_Entry[transients.size()]));
         }
         catch (XmlBlasterException ex) {
            log.severe(ME+"loadFromPeristence: Memory leak: problems removing " + transients.size() + " swapped transient entries form persistent store: " + ex.getMessage());
            return list.size();
         }

         return transients.size();
      }

      return 0;
   }


   /**
    * @see I_Queue#removeWithPriority(long, long, int, int)
    */
   public long removeWithPriority(long numOfEntries, long numOfBytes, int minPriority, int maxPriority)
      throws XmlBlasterException {
      List<I_Entry> list = null;
      boolean[] tmp = null;
      synchronized(this) {
         if (numOfEntries > Integer.MAX_VALUE)
            throw new XmlBlasterException(glob, ErrorCode.INTERNAL_ILLEGALARGUMENT, ME, "remove: too many entries to remove " + numOfEntries);
         list = peekWithPriority((int)numOfEntries, numOfBytes, minPriority, maxPriority);
         if (list == null || list.size() < 1) return 0L;
         tmp = removeRandomNoNotify((I_QueueEntry[])list.toArray(new I_QueueEntry[list.size()]));

      }
      if (this.notifiedAboutAddOrRemove) {
         for(int i=0; i<tmp.length; i++) {
            if (tmp[i]) {
               try {
                  ((I_Entry)list.get(i)).removed(this.queueId);
               }
               catch (Throwable ex) {
                  log.severe(ME+"removeWithPriority exception occured when notifying about removal. Reason: " + ex.toString());
                  ex.printStackTrace()
               }
            }
         }
      }
      this.storageSizeListenerHelper.invokeStorageSizeListener();
      long ret = 0L;
      for (int i=0; i < tmp.length; i++) if (tmp[i]) ret++;
      return ret;
   }

   /**
    * @see I_Queue#removeTransient()
    */
   public int removeTransient() throws XmlBlasterException {
      throw new XmlBlasterException(glob, ErrorCode.INTERNAL_NOTIMPLEMENTED, ME, "removeTransient not implemented");
   }


   /**
    * It returns the size of the queue. Note that this call will return the size
    * stored in cache, i.e. it will NOT make a call to the underlying DB.
    *
    * @see I_Queue#getNumOfEntries()
    */
   synchronized public long getNumOfEntries() {
      long ret = 0L;
      if (isPersistenceAvailable()) {
         ret = this.persistentQueue.getNumOfEntries();
         if (ret < 0L) return this.transientQueue.getNumOfEntries();
         ret += this.transientQueue.getNumOfEntries() - this.transientQueue.getNumOfPersistentEntries();
         return ret;
      }
      return this.transientQueue.getNumOfEntries();
   }


   /**
    * It returns the size of persistent entries in the queue. Note that this call will return the size
    * stored in cache, i.e. it will NOT make a call to the underlying DB.
    *
    * @see I_Queue#getNumOfPersistentEntries()
    */
   synchronized public long getNumOfPersistentEntries() {
      long ret = 0L;
      if (isPersistenceAvailable()) {
         ret = this.persistentQueue.getNumOfPersistentEntries();
         if (ret < 0L) return this.transientQueue.getNumOfEntries();
         return this.persistentQueue.getNumOfPersistentEntries();
      }
      return this.transientQueue.getNumOfPersistentEntries();
   }

   /**
    * @see I_Queue#getMaxNumOfEntries()
    */
   public long getMaxNumOfEntries() {
      if (isPersistenceAvailable())
         return this.persistentQueue.getMaxNumOfEntries();
      return this.transientQueue.getMaxNumOfEntries();
   }

   /**
    * @see I_Queue#getNumOfBytes()
    */
   synchronized public long getNumOfBytes() {
      long ret = 0L;
      if (isPersistenceAvailable()) {
         ret = this.persistentQueue.getNumOfBytes();
         if (ret < 0L) return this.transientQueue.getNumOfBytes();
         ret += this.transientQueue.getNumOfBytes() - this.transientQueue.getNumOfPersistentBytes();
         return ret;
      }
      return this.transientQueue.getNumOfBytes();
   }

   /**
    * @see I_Queue#getNumOfPersistentBytes()
    */
   synchronized public long getNumOfPersistentBytes() {
      long ret = 0L;
      if (isPersistenceAvailable()) {
         ret = this.persistentQueue.getNumOfPersistentBytes();
         // if a persistent queue return -1L it means it was not able to get the correct size
         if (ret < 0L) return this.transientQueue.getNumOfPersistentBytes();
         return ret;
      }
      return this.transientQueue.getNumOfPersistentBytes();
   }


   /**
    * @see I_Queue#getMaxNumOfBytes()
    */
   synchronized public long getMaxNumOfBytes() {
      if (isPersistenceAvailable())
         return this.persistentQueue.getMaxNumOfBytes();
      return this.transientQueue.getMaxNumOfBytes();
   }



   /**
    * Updates the given message queue entry with a new value. Note that this
    * can be used if an entry with the unique id already exists.
    * ?? Does this really make sense here since we need to store history ??
    * ?? Should we define a switch which can deactivate storage of history ??
    */
   public int update(I_QueueEntry queueEntry) throws XmlBlasterException
   {
      throw new XmlBlasterException(glob, ErrorCode.INTERNAL_NOTIMPLEMENTED, ME, "update not implemented");
   }


   /**
    * Clears everything and removes the queue (i.e. frees the associated table)
    * This method is not synchronized because of the callback in remove which must
    * be outside the synchronized.
    */
   public long clear() {
      if (this.notifiedAboutAddOrRemove) {
         long numOfEntries = getNumOfEntries();
         while (true) {
            long curr = getNumOfEntries();
            if (curr <= 0) break;
            //if (curr > 10000) curr = 10000; is protected by maxEntriesCached
            try {
               long count = removeNum(curr); // with callback to Entry for reference counting
               if (count == 0) break;
            }
            catch (Throwable e) {
               log.severe(ME+"Ignoring exception in clear(): " + e.toString());
            }
         }
         return numOfEntries;
      }
      else { // Entries don't have reference counting: The quick way:
         long numOfEntries = getNumOfEntries();
         try {
            if (this.persistentQueue != null) this.persistentQueue.clear();
         }
         catch (Throwable e) {
            log.severe(ME+"Ignoring exception in clear(): " + e.toString());
         }
         try {
            this.transientQueue.clear();
         }
         catch (Throwable e) {
            log.severe(ME+"Ignoring exception in clear(): " + e.toString());
         }
         return numOfEntries;
      }
   }

   /**
    * @see I_Queue#removeHead(I_QueueEntry)
    */
   public long removeHead(I_QueueEntry toEntry) throws XmlBlasterException {
      throw new XmlBlasterException(glob, ErrorCode.INTERNAL_NOTIMPLEMENTED, ME, "removeHead not implemented");
   }



   /**
    * Shutdown the implementation, sync with data store
    */
   public void shutdown() {
      synchronized (this) {
         if (log.isLoggable(Level.FINER)) log.finer(ME+"shutdown(isDown="+this.isDown+")");
         if (this.isDown) {
            return;
         }
         this.isDown = true;
         this.glob.unregisterMBean(this.mbeanHandle);
         long numTransients = getNumOfEntries() - getNumOfPersistentEntries();
         if (numTransients > 0) {
            log.warning(ME+"Shutting down cache queue which contains " + numTransients + " transient messages");
         }
  
         try {
            this.transientQueue.shutdown();
         }
         catch (Throwable ex) {
            log.severe(ME+"shutdown: exception when processing transient queue. Reason: " + ex.toString());
            ex.printStackTrace();
         }
  
         try {
            if (this.persistentQueue != null) this.persistentQueue.shutdown();
         }
         catch (Throwable ex) {
            log.severe(ME+"shutdown: exception when processing transient queue. Reason: " + ex.toString());
            ex.printStackTrace();
         }
         try {
   //         this.glob.getJdbcQueueManager(this.queueId).unregisterListener(this);
            if (this.persistentQueue != null) this.persistentQueue.unRegisterStorageProblemListener(this);
         }
         catch (Exception ex) {
            log.severe(ME+"could not unregister listener. Cause: " + ex.getMessage());
            ex.printStackTrace();
         }
      }
      this.storageSizeListenerHelper.invokeStorageSizeListener();
      removeStorageSizeListener(null);
      glob.getQueuePluginManager().cleanup(this);
   }

   public boolean isShutdown() {
      return this.isDown;
   }

   /**
    * JMX help
    * @return a human readable usage help string
    */
   public java.lang.String usage() {
      return "Manipulating the queue directly will most certainly destroy your data."
      +Global.getJmxUsageLinkInfo(this.getClass().getName(), null);
   }

   /**
    * @return A link for JMX usage
    */
   public java.lang.String getUsageUrl() {
      return Global.getJavadocUrl(this.getClass().getName(), null);
   }
  
   /* dummy to have a copy/paste functionality in jconsole */
   public void setUsageUrl(java.lang.String url) {}

   // JMX
   public final String toXml() {
      return toXml("");
   }

   /**
    * @return Internal state as an XML ASCII string
    */
   public final String toXml(String extraOffset) {
      StringBuffer sb = new StringBuffer(1000);
      if (extraOffset == null) extraOffset = "";
      String offset = Constants.OFFSET + extraOffset;

      sb.append(offset).append("<CacheQueueInterceptorPlugin id='").append(getStorageId().getId());
      sb.append("' type='").append(getType());
      sb.append("' version='").append(getVersion());
      sb.append("' maxEntriesCache='").append(this.transientQueue.getMaxNumOfEntries());
      sb.append("' maxBytesCache='").append(this.transientQueue.getMaxNumOfBytes());
      sb.append("' maxEntries='").append(getMaxNumOfEntries());
      sb.append("' maxBytes='").append(getMaxNumOfBytes());
      sb.append("' numOfEntries='").append(getNumOfEntries());
      sb.append("' numOfBytes='").append(getNumOfBytes());
      sb.append("'>");
      sb.append(this.transientQueue.toXml(extraOffset+Constants.INDENT));
      if (this.persistentQueue != null)
         sb.append(this.persistentQueue.toXml(extraOffset+Constants.INDENT));
      sb.append(offset).append("</CacheQueueInterceptorPlugin>");
      return sb.toString();
   }

   /**
    * Enforced by I_Plugin
    * @see org.xmlBlaster.util.plugin.I_Plugin#init(org.xmlBlaster.util.Global, PluginInfo)
    */
   public void init(org.xmlBlaster.util.Global glob, PluginInfo pluginInfo) {
      this.glob = glob;
      this.pluginInfo = pluginInfo;
//      this.pluginProperties = pluginInfo.getParameters();
   }

   /**
    * Enforced by I_Plugin
    * @return "CACHE"
    */
   public String getType() { return "CACHE"; }

   /**
    * Enforced by I_Plugin
    * @return "1.0"
    */
   public String getVersion() { return "1.0"; }

   /**
    * Enforced by I_StoragePlugin
    * @return the pluginInfo object.
    */
   public PluginInfo getInfo() { return this.pluginInfo; }

   /**
    * @see org.xmlBlaster.util.queue.I_StorageProblemNotifier#registerStorageProblemListener(I_StorageProblemListener)
    */
   synchronized public boolean registerStorageProblemListener(I_StorageProblemListener listener) {
      if (this.persistentQueue == null) return false;
      return this.persistentQueue.registerStorageProblemListener(listener);
   }


   /**
    * @see org.xmlBlaster.util.queue.I_StorageProblemNotifier#unRegisterStorageProblemListener(I_StorageProblemListener)
    */
   synchronized public boolean unRegisterStorageProblemListener(I_StorageProblemListener listener) {
      if (this.persistentQueue == null) return false;
      return this.persistentQueue.unRegisterStorageProblemListener(listener);
   }

   /**
    * @see I_Queue#addStorageSizeListener(I_StorageSizeListener)
    */
   public void addStorageSizeListener(I_StorageSizeListener listener) {
      this.storageSizeListenerHelper.addStorageSizeListener(listener);
   }
  
   /**
    * @see I_Queue#removeStorageSizeListener(I_StorageSizeListener)
    */
   public void removeStorageSizeListener(I_StorageSizeListener listener) {
      this.storageSizeListenerHelper.removeStorageSizeListener(listener);
   }
  
   /**
    * @see I_Queue#hasStorageSizeListener(I_StorageSizeListener)
    */
   public boolean hasStorageSizeListener(I_StorageSizeListener listener) {
      return this.storageSizeListenerHelper.hasStorageSizeListener(listener);
   }

   /**
    * @see I_Storage#getStorageSizeListeners()
    */
   public I_StorageSizeListener[] getStorageSizeListeners() {
      return storageSizeListenerHelper.getStorageSizeListeners();
   }


   /**
    * @see I_Queue#embeddedObjectsToXml(OutputStream, Properties)
    */
   public long embeddedObjectsToXml(OutputStream out, Properties props) throws Exception {
      I_Queue queue = this.persistentQueue;
      if (queue != null) {
         return queue.embeddedObjectsToXml(out, null);
      }
      log.warning(ME+"Sorry, dumping transient entries to '" + out + "' is not implemented");
      return 0;
   }
  
   /**
    * @see I_AdminQueue#dumpEmbeddedObjectsToFile(String)
    */
   public String dumpEmbeddedObjectsToFile(String fileName) throws Exception {
      if (fileName == null || fileName.equalsIgnoreCase("String")) {
         fileName = this.queueId.getStrippedId() + ".xml";
      }
      File to_file = new File(fileName);
      if (to_file.getParent() != null) {
         to_file.getParentFile().mkdirs();
      }
      FileOutputStream out = new FileOutputStream(to_file);
      out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>".getBytes());
      out.write(("\n<"+this.queueId.getRelatingType()+">").getBytes());
      Properties props = new Properties();
      props.put(Constants.TOXML_FORCEREADABLE, ""+true)// to be human readable (minimize base64)
      props.put(Constants.TOXML_ENCLOSINGTAG, "publish"); // to look similar to XmlScript
      long count = embeddedObjectsToXml(out, props);
      out.write(("\n</"+this.queueId.getRelatingType()+">").getBytes());
      out.close();
      return "Dumped " + count + " entries to '" + to_file.toString() + "'";
   }
}
TOP

Related Classes of org.xmlBlaster.util.queue.cache.CacheQueueInterceptorPlugin

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.