/*------------------------------------------------------------------------------
Name: JdbcQueueCommonTablePlugin.java
Project: xmlBlaster.org
Copyright: xmlBlaster.org, see xmlBlaster-LICENSE file
------------------------------------------------------------------------------*/
package org.xmlBlaster.util.queue.jdbc;
import java.util.logging.Logger;
import java.util.logging.Level;
import org.xmlBlaster.util.XmlBlasterException;
import org.xmlBlaster.util.def.ErrorCode;
import org.xmlBlaster.util.Global;
import org.xmlBlaster.util.queue.I_EntryFilter;
import org.xmlBlaster.util.queue.I_StorageSizeListener;
import org.xmlBlaster.util.queue.I_Storage;
import org.xmlBlaster.util.queue.StorageId;
import org.xmlBlaster.util.queue.I_Queue;
import org.xmlBlaster.util.queue.I_QueueEntry;
import org.xmlBlaster.util.queue.I_Entry;
import org.xmlBlaster.util.queue.I_QueuePutListener;
import org.xmlBlaster.util.queue.ReturnDataHolder;
import org.xmlBlaster.util.queue.I_StoragePlugin;
import org.xmlBlaster.util.queue.StorageSizeListenerHelper;
import org.xmlBlaster.util.plugin.PluginInfo;
import org.xmlBlaster.util.qos.storage.QueuePropertyBase;
import org.xmlBlaster.util.def.Constants;
import org.xmlBlaster.engine.ServerScope;
import org.xmlBlaster.engine.msgstore.I_Map;
import org.xmlBlaster.engine.msgstore.I_MapEntry;
import org.xmlBlaster.engine.msgstore.I_ChangeCallback;
import org.xmlBlaster.util.queue.I_StorageProblemListener;
import java.io.OutputStream;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Properties;
/**
* Persistence queue implementation on a DB based on JDBC.
* <p>
* Loaded via xmlBlaster.properties, there are three implementation.
* <p>
* org.xmlBlaster.util.queue.jdbc.JdbcQueueCommonTablePlugin<br />
* was the operational variant up to v1.6.2+ (2008-10) using one XB_ENTRIES table
* </p>
* <p>
* org.xmlBlaster.util.queue.jdbc.JdbcQueue<br />
* is the new implementation since v1.6.3 using three tables XBSTORE, XBREF, XBMEAT
* </p>
* Example:
* <pre>
* JdbcStorage[postgres]=org.xmlBlaster.util.queue.jdbc.JdbcQueue,\
url=jdbc:postgresql://localhost:5432/test,\
user=postgres,\
password=,\
connectionPoolSize=1,\
connectionBusyTimeout=90000,\
maxWaitingThreads=300,\
tableNamePrefix=XB_,\
entriesTableName=ENTRIES,\
dbAdmin=true
* </pre>
* @author michele@laghi.eu
* @author xmlBlaster@marcelruff.info
* @see <a href="http://www.xmlBlaster.org/xmlBlaster/doc/requirements/queue.jdbc.commontable.html">The queue.jdbc.commontable requirement</a>
* @see JdbcQueue
*/
public final class JdbcQueueCommonTablePlugin implements I_Queue, I_StoragePlugin, I_Map
{
private String ME;
private StorageId storageId;
private boolean notifiedAboutAddOrRemove = false;
private Global glob;
private static Logger log = Logger.getLogger(JdbcQueueCommonTablePlugin.class.getName());
private QueuePropertyBase property;
private CommonTableDatabaseAccessor manager = null;
private I_QueuePutListener putListener;
// to set it to -999L makes it easier to identify than -1L
private long numOfEntries = -999L;
private long numOfPersistentEntries = -999L;
private long numOfPersistentBytes = -999L;
private long numOfBytes = -999L;
boolean isDown = true;
/** Monitor object used to synchronize the count of sizes */
private Object modificationMonitor = new Object();
private PluginInfo pluginInfo = null;
private boolean debug = false;
private int entryCounter;
private static boolean isWarned;
private StorageSizeListenerHelper storageSizeListenerHelper;
public JdbcQueueCommonTablePlugin() {
if (!isWarned) {
isWarned = true;
log.warning("Be aware: You are using the OLD JdbcQueueCommonTablePlugin database persistence layer with the 'xb_entires' table which was the default up to xmlBlaster v1.6.4. " +
"Please consider upgrading to the new persistence schema. Details: http://www.xmlblaster.org/xmlBlaster/doc/requirements/queue.jdbc.html");
}
this.storageSizeListenerHelper = new StorageSizeListenerHelper(this);
}
public boolean isTransient() {
return false;
}
/**
* This method resets all cached sizes and counters. While testing it
* could be invoked before each public invocation to see which method fails ...
*/
private final void resetCounters() {
try {
this.numOfPersistentBytes = -999L;
this.numOfPersistentEntries = -999L;
this.numOfBytes = -999L;
this.numOfEntries = -999L;
this.numOfPersistentBytes = getNumOfPersistentBytes_(true);
this.numOfPersistentEntries = getNumOfPersistentEntries_(true);
this.numOfBytes = getNumOfBytes_();
this.numOfEntries = getNumOfEntries_();
}
catch (XmlBlasterException ex) {
if (log.isLoggable(Level.FINE)) log.fine("resetCounters exception occured: " + ex.getMessage());
}
}
/**
* Check is storage is big enough for entry
* @param mapEntry may not be null
* @return null There is space (otherwise the error text is returned)
*/
private String spaceLeft(long numOfEntries, long sizeInBytes) {
// allow one owerload only ...
numOfEntries = 0L;
sizeInBytes = 0L;
if (this.property == null) {
return "Storage framework is down, current settings are" + toXml("");
}
if ((numOfEntries + getNumOfEntries()) > getMaxNumOfEntries())
return "Queue overflow (number of entries), " + getNumOfEntries() +
" entries are in queue, try increasing property '" +
this.property.getPropName("maxEntries") + "' and '" +
this.property.getPropName("maxEntriesCache") + "', current settings are" + toXml("");
if ((sizeInBytes + getNumOfBytes()) > getMaxNumOfBytes())
return "Queue overflow, " + getMaxNumOfBytes() +
" bytes are in queue, try increasing property '" +
this.property.getPropName("maxBytes") + "' and '" +
this.property.getPropName("maxBytesCache") + "', current settings are" + toXml("");
return null;
}
/**
* Calculates the size in bytes of all entries in the array.
*/
/*
private long calculateSizeInBytes(I_QueueEntry[] entries) {
long sum = 0L;
for (int i=0; i<entries.length; i++) {
sum += entries[i].getSizeInBytes();
}
return sum;
}
*/
/**
* Returns a JdbcManagerCommonTable for a specific queue. It strips the queueId to
* find out to which manager it belongs. If such a manager does not exist
* yet, it is created and initialized.
* A queueId must be of the kind: cb:some/id/or/someother
* where the important requirement here is that it contains a ':' character.
* text on the left side of the separator (in this case 'cb') tells which
* kind of queue it is: for example a callback queue (cb) or a client queue.
*/
protected CommonTableDatabaseAccessor getJdbcQueueManagerCommonTable(PluginInfo pluginInfo)
throws XmlBlasterException {
String location = ME + "/type '" + pluginInfo.getType() + "' version '" + pluginInfo.getVersion() + "'";
String managerName = pluginInfo.toString(); // + "-" + pluginInfo.getTypeVersion();
Object obj = this.glob.getObjectEntry(managerName);
CommonTableDatabaseAccessor manager = null;
try {
if (obj == null) {
synchronized (CommonTableDatabaseAccessor.class) {
obj = this.glob.getObjectEntry(managerName); // could have been initialized meanwhile
if ( obj == null) {
JdbcConnectionPool pool = new JdbcConnectionPool();
pool.initialize(this.glob, pluginInfo.getParameters());
boolean useJdbcManagerDelegate = glob.get("xmlBlaster/useJdbcManagerDelegate", true, null, pluginInfo);
manager = (useJdbcManagerDelegate) ?
new CommonTableDatabaseAccessorDelegate(pool, this.glob.getEntryFactory(), managerName, this) :
new CommonTableDatabaseAccessor(pool, this.glob.getEntryFactory(), managerName, this);
pool.registerStorageProblemListener(manager);
manager.setUp();
if (log.isLoggable(Level.FINE)) log.fine("Created JdbcManagerCommonTable instance for storage plugin configuration '" + managerName + "'");
this.glob.addObjectEntry(managerName, manager);
}
else manager = (CommonTableDatabaseAccessor)obj;
}
}
else manager = (CommonTableDatabaseAccessor)obj;
if (!manager.getPool().isInitialized()) {
manager.getPool().initialize(this.glob, pluginInfo.getParameters());
if (log.isLoggable(Level.FINE)) log.fine("Initialized JdbcManager pool for storage class '" + managerName + "'");
}
}
catch (ClassNotFoundException ex) {
log.severe("getJdbcCommonTableQueueManager class not found: " + ex.getMessage());
ex.printStackTrace();
throw new XmlBlasterException(this.glob, ErrorCode.RESOURCE_DB_UNAVAILABLE, location, "getJdbcQueueCommonTableManager: class not found when initializing the connection pool", ex);
}
catch (SQLException ex) {
throw new XmlBlasterException(this.glob, ErrorCode.RESOURCE_DB_UNAVAILABLE, location, "getJdbcCommonTableQueueManager: sql exception when initializing the connection pool", ex);
}
catch (Throwable ex) {
if (log.isLoggable(Level.FINE)) {
log.fine("getJdbcQueueCommonTableManager internal exception: " + ex.toString());
ex.printStackTrace();
}
throw new XmlBlasterException(this.glob, ErrorCode.INTERNAL_UNKNOWN, location, "getJdbcQueueManager: throwable when initializing the connection pool", ex);
}
if (this.glob.getWipeOutDB()) {
synchronized (this.glob) {
if (this.glob.getWipeOutDB()) {
manager.wipeOutDB(true);
this.glob.setWipeOutDB(false);
}
}
}
return manager;
}
/**
* 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) {
this.property = null;
setProperties(userData);
this.ME = this.getClass().getName() + "-" + uniqueQueueId;
this.storageId = uniqueQueueId;
if (log.isLoggable(Level.FINER)) log.finer("initialize '" + this.storageId + "'");
if (this.property != null && this.glob.isServerSide() != this.property.getGlobal().isServerSide()) {
log.severe("Incompatible globals this.property.getGlobal().isServerSide()=" + this.property.getGlobal().isServerSide() + ": " + Global.getStackTraceAsString(null));
}
this.glob = this.property.getGlobal();
this.manager = getJdbcQueueManagerCommonTable(this.pluginInfo);
EntryCount entryCount = this.manager.getNumOfAll(getStorageId().getStrippedId());
setEntryCount(entryCount);
//this.numOfEntries = this.manager.getNumOfEntries(getStorageId().getStrippedId());
//this.numOfBytes = this.manager.getNumOfBytes(this.storageId.getStrippedId());
//this.numOfPersistentEntries = this.manager.getNumOfPersistents(getStorageId().getStrippedId());
//this.numOfPersistentBytes = this.manager.getSizeOfPersistents(getStorageId().getStrippedId());
this.isDown = false;
this.manager.registerQueue(this);
if (log.isLoggable(Level.FINE)) log.fine("Successful initialized");
}
boolean dbg = this.glob.getProperty().get("queue/debug", false);
if (dbg == true) this.property.setDebug(true);
this.debug = this.property.getDebug();
if (this.debug) {
log.warning("initialize: debugging is enabled");
}
}
/**
* @see I_Queue#setProperties(Object)
*/
public void setProperties(Object userData) throws XmlBlasterException {
if (userData == null) return;
QueuePropertyBase newProp;
try {
newProp = (QueuePropertyBase)userData;
}
catch(Throwable e) {
log.severe("Can't configure queue, your properties are invalid: " + e.toString());
e.printStackTrace();
return;
}
// sync necessary?
/* 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;
}
/**
* 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)
*/
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)
*/
public void removePutListener(I_QueuePutListener l) {
this.putListener = null;
}
/**
* 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 ArrayList getEntries(I_EntryFilter entryFilter) throws XmlBlasterException {
return this.manager.getEntries(getStorageId(), -1, -1L, entryFilter);
}
/**
* @see I_Queue#put(I_QueueEntry, boolean)
*/
public void put(I_QueueEntry queueEntry, boolean ignorePutInterceptor)
throws XmlBlasterException
{
if (queueEntry == null) return;
if ((this.putListener != null) && (!ignorePutInterceptor)) {
// Is an interceptor registered (and not bypassed) ?
if (this.putListener.putPre(queueEntry) == false)
return;
}
put(queueEntry);
if (this.putListener != null && !ignorePutInterceptor) {
this.putListener.putPost(queueEntry);
}
}
/**
* Internally used for I_MapEntry and I_QueueEntry
* @return true on success
*/
private boolean put(I_Entry entry) throws XmlBlasterException {
boolean ret = false;
synchronized (this.modificationMonitor) {
String exTxt = null;
if ((exTxt=spaceLeft(1, entry.getSizeInBytes())) != null)
throw new XmlBlasterException(glob, ErrorCode.RESOURCE_OVERFLOW_QUEUE_ENTRIES, ME, exTxt);
if (getNumOfBytes_() > getMaxNumOfBytes()) {
throw new XmlBlasterException(glob, ErrorCode.RESOURCE_OVERFLOW_QUEUE_BYTES, ME, "put: the maximum number of bytes reached." +
" Number of bytes=" + this.numOfBytes + " maxmimum number of bytes=" + getMaxNumOfBytes() + " status: " + this.toXml(""));
}
try {
if (this.manager.addEntry(this.storageId.getStrippedId(), entry)) {
this.numOfEntries++;
this.numOfBytes += entry.getSizeInBytes();
if (entry.isPersistent()) {
this.numOfPersistentEntries++;
this.numOfPersistentBytes += entry.getSizeInBytes();
}
ret = true;
}
}
catch (XmlBlasterException ex) {
resetCounters();
throw ex;
}
}
this.storageSizeListenerHelper.invokeStorageSizeListener();
return ret;
}
/**
* @see I_Queue#put(I_QueueEntry[], boolean)
*/
public void put(I_QueueEntry[] queueEntries, boolean ignorePutInterceptor)
throws XmlBlasterException {
XmlBlasterException ex0 = null;
if (queueEntries == null) return;
if ((this.putListener != null) &&(!ignorePutInterceptor)) {
// Is an interceptor registered (and not bypassed) ?
if (this.putListener.putPre(queueEntries) == false)
return;
}
synchronized (this.modificationMonitor) {
String exTxt = null;
if ((exTxt=spaceLeft(queueEntries.length, /*calculateSizeInBytes(queueEntries)*/ 0L)) != null)
throw new XmlBlasterException(glob, ErrorCode.RESOURCE_OVERFLOW_QUEUE_ENTRIES, ME, exTxt);
if (getNumOfBytes_() > getMaxNumOfBytes()) {
throw new XmlBlasterException(glob, ErrorCode.RESOURCE_OVERFLOW_QUEUE_BYTES, ME, "put[]: the maximum number of bytes reached." +
" Number of bytes=" + this.numOfBytes + " maxmimum number of bytes=" + getMaxNumOfBytes() + " status: " + this.toXml(""));
}
try {
int[] help = this.manager.addEntries(this.storageId.getStrippedId(), queueEntries);
for (int i=0; i < queueEntries.length; i++) {
// boolean isProcessed = this.manager.addEntry(this.storageId.getStrippedId(), queueEntries[i]);
boolean isProcessed = help[i] > 0 || help[i] == -2; // !!! JDK 1.4 only: Statement.SUCCESS_NO_INFO = -2;
if (log.isLoggable(Level.FINE)) {
log.fine("put(I_Entry[]) the entry nr. " + i + " returned '" + help[i] + "'");
}
if (isProcessed) {
this.numOfEntries++;
this.numOfBytes += queueEntries[i].getSizeInBytes();
if (queueEntries[i].isPersistent()) {
this.numOfPersistentEntries++;
this.numOfPersistentBytes += queueEntries[i].getSizeInBytes();
}
}
}
}
catch (XmlBlasterException ex) {
ex0 = ex;
resetCounters();
}
if (ex0 != null)
throw ex0;
}
if (this.putListener != null && !ignorePutInterceptor) {
this.putListener.putPost(queueEntries);
}
this.storageSizeListenerHelper.invokeStorageSizeListener();
}
/**
* Returns the unique ID of this queue
*/
public StorageId getStorageId() {
return this.storageId;
}
/**
* Currently not supported by I_Queue.
*/
public I_QueueEntry take() throws XmlBlasterException
{
// note that this method could be drastically improved
// however it is unlikely to be used so I avoid that tuning now
synchronized (this.modificationMonitor) {
I_QueueEntry ret = this.peek();
this.removeRandom(ret.getUniqueId());
return ret;
}
}
/**
* @see I_Queue#takeWithPriority(int,long,int,int)
*/
public ArrayList takeWithPriority(int numOfEntries, long numOfBytes, int minPriority, int maxPriority)
throws XmlBlasterException {
if (numOfEntries == 0) return new ArrayList();
synchronized (this.modificationMonitor) {
ArrayList ret = this.peekWithPriority(numOfEntries, numOfBytes, minPriority, maxPriority);
this.removeRandom( (I_QueueEntry[])ret.toArray(new I_QueueEntry[ret.size()]) );
return ret;
}
}
/**
* Currently not supported by I_Queue.
*/
public ArrayList take(int numOfEntries, long numOfBytes) throws XmlBlasterException {
if (numOfEntries == 0) return new ArrayList();
ArrayList ret = null;
I_EntryFilter entryFilter = null;
synchronized(this.modificationMonitor) {
try {
ret = this.manager.getEntries(getStorageId(), numOfEntries, numOfBytes, entryFilter);
long ids[] = new long[ret.size()];
for (int i=0; i < ids.length; i++)
ids[i] = ((I_QueueEntry)ret.get(i)).getUniqueId();
boolean tmp[] = this.manager.deleteEntries(getStorageId().getStrippedId(), ids);
for (int i=0; i < tmp.length; i++) {
if (tmp[i]) this.numOfEntries--;
}
}
finally {
resetCounters();
}
}
this.storageSizeListenerHelper.invokeStorageSizeListener();
return ret;
}
/**
* @see I_Queue#takeLowest(int, long, I_QueueEntry, boolean)
*/
public ArrayList takeLowest(int numOfEntries, long numOfBytes, I_QueueEntry limitEntry, boolean leaveOne)
throws XmlBlasterException {
// I could change the concept here by just checking if an entry with the
// given uniqueId is found the search algorithm should break. This would
// increase performance. However this method is probably never called on
// this particular implementation.
long minUniqueId = 0L;
int maxPriority = Integer.MAX_VALUE;
if (limitEntry != null) {
minUniqueId = limitEntry.getUniqueId();
maxPriority = limitEntry.getPriority();
}
ReturnDataHolder ret = null;
synchronized(this.modificationMonitor) {
try {
ret = this.manager.getAndDeleteLowest(getStorageId(), numOfEntries, numOfBytes, maxPriority, minUniqueId, leaveOne, true);
this.numOfBytes -= ret.countBytes;
this.numOfEntries -= ret.countEntries;
this.numOfPersistentBytes -= ret.countPersistentBytes;
this.numOfPersistentEntries -= ret.countPersistentEntries;
}
catch (XmlBlasterException ex) {
resetCounters();
throw ex;
}
}
this.storageSizeListenerHelper.invokeStorageSizeListener();
if (ret == null) return null;
return ret.list;
}
/**
* @see I_Queue#peekLowest(int, long, I_QueueEntry, boolean)
*/
public ArrayList peekLowest(int numOfEntries, long numOfBytes, I_QueueEntry limitEntry, boolean leaveOne)
throws XmlBlasterException {
long minUniqueId = 0L;
int maxPriority = Integer.MAX_VALUE;
if (limitEntry != null) {
minUniqueId = limitEntry.getUniqueId();
maxPriority = limitEntry.getPriority();
}
ReturnDataHolder ret = this.manager.getAndDeleteLowest(getStorageId(), numOfEntries, numOfBytes, maxPriority, minUniqueId, leaveOne, false);
return ret.list;
}
/**
* @see I_Queue#peek()
*/
public I_QueueEntry peek() throws XmlBlasterException
{
I_EntryFilter entryFilter = null;
ArrayList ret = this.manager.getEntries(getStorageId(), 1, -1L, entryFilter);
if (ret.size() < 1) return null;
return (I_QueueEntry)ret.get(0);
}
/**
* @see I_Queue#peek(int,long)
*/
public ArrayList peek(int numOfEntries, long numOfBytes) throws XmlBlasterException {
if (numOfEntries == 0) return new ArrayList();
I_EntryFilter entryFilter = null;
ArrayList ret = this.manager.getEntries(getStorageId(), numOfEntries, numOfBytes, entryFilter);
return ret;
}
public ArrayList peekStartAt(int numOfEntries, long numOfBytes, I_QueueEntry firstEntryExlusive) throws XmlBlasterException {
// TODO
return peek(numOfEntries, numOfBytes);
}
/**
* @see I_Queue#peekSamePriority(int, long)
*/
public ArrayList peekSamePriority(int numOfEntries, long numOfBytes) throws XmlBlasterException {
if (numOfEntries == 0) return new ArrayList();
ArrayList ret = this.manager.getEntriesBySamePriority(getStorageId(), numOfEntries, numOfBytes);
return ret;
}
/**
* @see I_Queue#peekWithPriority(int, long, int, int)
*/
public ArrayList peekWithPriority(int numOfEntries, long numOfBytes, int minPriority, int maxPriority) throws XmlBlasterException {
if (numOfEntries == 0) return new ArrayList();
I_EntryFilter entryFilter = null;
ArrayList ret = this.manager.getEntriesByPriority(getStorageId(), numOfEntries, numOfBytes, minPriority, maxPriority, entryFilter);
return ret;
}
/**
* @see I_Queue#peekWithLimitEntry(I_QueueEntry)
* @deprecated
*/
public ArrayList peekWithLimitEntry(I_QueueEntry limitEntry) throws XmlBlasterException {
if (log.isLoggable(Level.FINER)) log.finer("peekWithLimitEntry called");
if (limitEntry == null) return new ArrayList();
return this.manager.getEntriesWithLimit(getStorageId(), limitEntry);
}
/**
* @see I_Queue#removeWithLimitEntry(I_QueueEntry, boolean)
*/
public long removeWithLimitEntry(I_QueueEntry limitEntry, boolean inclusive) throws XmlBlasterException {
if (log.isLoggable(Level.FINER)) log.finer("removeWithLimitEntry called");
if (limitEntry == null) return 0L;
long ret = 0L;
synchronized(this.modificationMonitor) {
try {
ret = this.manager.removeEntriesWithLimit(getStorageId(), limitEntry, inclusive);
if (ret != 0) { // since we are not able to calculate the size in the cache we have to recalculate it
resetCounters();
}
}
catch (XmlBlasterException ex) {
resetCounters();
throw ex;
}
}
this.storageSizeListenerHelper.invokeStorageSizeListener();
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 {
ReturnDataHolder ret = null;
synchronized(this.modificationMonitor) {
try {
ret = this.manager.deleteFirstEntries(getStorageId().getStrippedId(), 1, -1L);
this.numOfEntries -= (int)ret.countEntries;
this.numOfBytes -= ret.countBytes;
this.numOfPersistentBytes = -999L;
this.numOfPersistentEntries = -999L;
this.numOfPersistentBytes = getNumOfPersistentBytes_(true);
this.numOfPersistentEntries = getNumOfPersistentEntries_(true);
}
catch (XmlBlasterException ex) {
resetCounters();
throw ex;
}
}
this.storageSizeListenerHelper.invokeStorageSizeListener();
if (ret == null) return 0;
return (int)ret.countEntries;
}
/**
* Removes max numOfEntries messages.
* This method does not block.
* @param numOfEntries 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 {
if (numOfEntries == 0) return 0L;
ReturnDataHolder ret = null;
synchronized(this.modificationMonitor) {
try {
ret = this.manager.deleteFirstEntries(getStorageId().getStrippedId(), numOfEntries, numOfBytes);
this.numOfEntries -= (int)ret.countEntries;
this.numOfBytes -= ret.countBytes;
this.numOfPersistentBytes = -999L;
this.numOfPersistentEntries = -999L;
this.numOfPersistentBytes = getNumOfPersistentBytes_(true);
this.numOfPersistentEntries = getNumOfPersistentEntries_(true);
}
catch (XmlBlasterException ex) {
resetCounters();
throw ex;
}
}
this.storageSizeListenerHelper.invokeStorageSizeListener();
if (ret == null) return 0L;
return ret.countEntries;
}
/**
* 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 {
long[] args = new long[1];
args[0] = dataId;
if (removeRandom(args)[0]) return 1;
else return 0;
}
/**
* Removes the given entries.
* @param msgQueueEntry the entry to erase.
*/
public boolean[] removeRandom(long[] dataIdArray) throws XmlBlasterException {
ArrayList list = this.manager.getEntries(getStorageId(), dataIdArray);
return removeRandom((I_Entry[])list.toArray(new I_Entry[list.size()]));
}
/**
* @see I_Queue#removeRandom(I_Entry)
*/
public int removeRandom(I_Entry entry) throws XmlBlasterException {
if (entry == null) return 0;
long id = entry.getUniqueId();
long currentAmount = entry.getSizeInBytes();
long currentPersistentSize = 0L;
long currentPersistentEntries = 0L;
if (entry.isPersistent()) {
currentPersistentSize += currentAmount;
currentPersistentEntries = 1L;
}
int ret = 0;
synchronized(this.modificationMonitor) {
ret = this.manager.deleteEntry(getStorageId().getStrippedId(), id);
if (ret > 0) { // then we need to retrieve the values
this.numOfEntries--;
this.numOfBytes -= currentAmount;
this.numOfPersistentBytes -= currentPersistentSize;
this.numOfPersistentEntries -= currentPersistentEntries;
}
}
this.storageSizeListenerHelper.invokeStorageSizeListener();
return ret;
}
/**
* @see I_Queue#removeRandom(I_Entry[])
*/
public boolean[] removeRandom(I_Entry[] queueEntries) throws XmlBlasterException {
if (queueEntries == null || queueEntries.length == 0) return new boolean[0];
long[] ids = new long[queueEntries.length];
long currentAmount = 0L;
long currentPersistentSize = 0L;
long currentPersistentEntries = 0L;
for (int i=0; i < ids.length; i++) {
ids[i] = queueEntries[i].getUniqueId();
currentAmount += queueEntries[i].getSizeInBytes();
if (queueEntries[i].isPersistent()) {
currentPersistentSize += queueEntries[i].getSizeInBytes();
currentPersistentEntries++;
}
}
boolean[] tmp = null;
synchronized(this.modificationMonitor) {
try {
/*
int[] tmp = this.manager.deleteEntriesBatch(getStorageId().getStrippedId(), ids);
long sum = 0;
for (int i=0; i < tmp.length; i++) {
if (log.isLoggable(Level.FINE)) log.trace(ME, "removeRandom: entry '" + i + "' is '" + tmp[i]);
ret[i] = tmp[i] > 0 || tmp[i] == -2; // !!! JDK 1.4 only: Statement.SUCCESS_NO_INFO = -2;
if (ret[i]) sum++;
}
*/
tmp = this.manager.deleteEntries(getStorageId().getStrippedId(), ids);
long sum = 0;
for (int i=0; i < tmp.length; i++) {
if (tmp[i]) sum++;
}
if (log.isLoggable(Level.FINE))
log.fine("randomRemove: the number of removed entries is '" + sum + "'");
this.numOfEntries -= sum;
if ((int)sum != queueEntries.length) { // then we need to retrieve the values
resetCounters(); // now it can be optimized since boolean[] is given back
}
else {
this.numOfBytes -= currentAmount;
this.numOfPersistentBytes -= currentPersistentSize;
this.numOfPersistentEntries -= currentPersistentEntries;
}
}
catch (XmlBlasterException ex) {
resetCounters();
throw ex;
}
}
this.storageSizeListenerHelper.invokeStorageSizeListener();
return tmp;
}
/**
* @see I_Queue#removeWithPriority(long, long, int, int)
*/
public long removeWithPriority(long numOfEntries, long numOfBytes, int minPriority, int maxPriority)
throws XmlBlasterException {
ArrayList array = this.peekWithPriority((int)numOfEntries, numOfBytes, minPriority, maxPriority);
boolean ret[] = removeRandom((I_QueueEntry[])array.toArray(new I_QueueEntry[array.size()]));
long count = 0L;
for (int i=0; i < ret.length; i++) if (ret[i]) count++;
return count;
}
/*
* Not needed anymore
* @see I_Queue#removeTransient()
public int removeTransient() throws XmlBlasterException {
int ret = 0;
synchronized(this.modificationMonitor) {
try {
ret = this.manager.deleteAllTransient(getStorageId().getStrippedId());
this.numOfEntries = this.numOfPersistentEntries;
this.numOfBytes = this.numOfPersistentBytes;
}
catch (XmlBlasterException ex) {
resetCounters();
throw ex;
}
}
this.storageSizeListenerHelper.invokeStorageSizeListener();
return ret;
}
*/
private void setEntryCount(EntryCount entryCount) {
this.numOfEntries = entryCount.numOfEntries;
this.numOfPersistentEntries = entryCount.numOfPersistentEntries;
this.numOfBytes = entryCount.numOfBytes;
this.numOfPersistentBytes = entryCount.numOfPersistentBytes;
}
/**
* It returns the size of the queue. Note that this call will return the size
* stored in cache.
* In case this value is negative (which means a previous attempt to read from the
* DB failed) it will synchronize against the DB by making a call to the DB.
* If that fails it will return -1L.
* If the log DUMP is set to true, then a refresh of the cache is done by every invocation
* and if the cached value is different from the real value an error is written.
*
* @see I_Queue#getNumOfEntries()
* @exception XmlBlasterException if number is not retrievable
*/
private final long getNumOfEntries_() throws XmlBlasterException {
if (this.numOfEntries > -1L && !this.debug) return this.numOfEntries;
synchronized (this.modificationMonitor) {
long oldValue = this.numOfEntries;
EntryCount entryCount = this.manager.getNumOfAll(getStorageId().getStrippedId());
setEntryCount(entryCount);
//this.numOfEntries = this.manager.getNumOfEntries(getStorageId().getStrippedId());
if (this.debug) {
if (oldValue != this.numOfEntries && oldValue != -999L) { // don't log if explicitly set the oldValue
String txt = "getNumOfEntries: an inconsistency occured between the cached value and the real value of 'numOfEntries': it was '" + oldValue + "' but should have been '" + this.numOfEntries + "'";
throw new XmlBlasterException(this.glob, ErrorCode.INTERNAL_UNKNOWN, ME, txt + toXml(""));
}
}
else if (log.isLoggable(Level.FINE))
log.fine("getNumOfEntries_ old (cached) value: '" + oldValue + "' new (real) value: '" + this.numOfEntries + "'");
return this.numOfEntries;
}
}
/**
* It returns the size of the queue. Note that this call will return the size
* stored in cache.
* In case this value is -1L (which means a previous attempt to read from the
* DB failed) it will synchronize against the DB by making a call to the DB.
* If that fails it will return -1L.
*
* @see I_Queue#getNumOfEntries()
*/
public long getNumOfEntries() {
try {
return getNumOfEntries_();
}
catch (XmlBlasterException ex) {
log.severe("getNumOfEntries, exception: " + ex.getMessage());
return this.numOfEntries;
}
}
/**
* It returns the number of persistent entries in the queue.
* In case this value is -1L (which means a previous attempt to read from the
* DB failed) it will synchronize against the DB by making a call to the DB.
* If that fails it will return -1L.
*
* @param verbose If true we throw an exception on errors, if false we ignore the error silently
* @see I_Queue#getNumOfPersistentEntries()
*/
private long getNumOfPersistentEntries_(boolean verbose) throws XmlBlasterException {
if (this.numOfPersistentEntries > -1L && !this.debug) return this.numOfPersistentEntries;
synchronized (this.modificationMonitor) {
try {
long oldValue = this.numOfPersistentEntries;
EntryCount entryCount = this.manager.getNumOfAll(getStorageId().getStrippedId());
setEntryCount(entryCount);
//this.numOfPersistentEntries = this.manager.getNumOfPersistents(getStorageId().getStrippedId());
if (this.debug) {
if (oldValue != this.numOfPersistentEntries && oldValue != -999L) { // don't log if explicitly set the oldValue
String txt = "getNumOfPersistentEntries: an inconsistency occured between the cached value and the real value of 'numOfPersistentEntries': it was '" + oldValue + "' but should have been '" + this.numOfPersistentEntries + "'";
throw new XmlBlasterException(this.glob, ErrorCode.INTERNAL_UNKNOWN, ME, txt + toXml(""));
}
}
else if (log.isLoggable(Level.FINE))
log.fine("getNumOfPersistentEntries_ old (cached) value: '" + oldValue + "' new (real) value: '" + this.numOfPersistentEntries + "'");
return this.numOfPersistentEntries;
}
catch (XmlBlasterException ex) {
if (verbose) { // If called from toXml() we need to suppress this exeption because we here call toXml() again
throw ex; // the verbose flag is probably not needed anymore ...
}
return -1L;
}
}
}
/**
* It returns the number of persistent entries in the queue.
* In case this value is -1L (which means a previous attempt to read from the
* DB failed) it will synchronize against the DB by making a call to the DB.
* If that fails it will return -1L.
*
* @see I_Queue#getNumOfPersistentEntries()
*/
public long getNumOfPersistentEntries() {
try {
return getNumOfPersistentEntries_(true);
}
catch (XmlBlasterException ex) {
log.severe("getNumOfEntries, exception: " + ex.getMessage());
return this.numOfPersistentEntries;
}
}
/**
* @see I_Queue#getMaxNumOfEntries()
*/
public long getMaxNumOfEntries() {
return this.property.getMaxEntries();
}
/**
* It returns the size of the queue. Note that this call will return the size
* stored in cache.
* In case this value is -1L (which means a previous attempt to read from the
* DB failed) it will synchronize against the DB by making a call to the DB.
* If that fails it will return -1L.
*
* @see I_Queue#getNumOfBytes()
*/
private long getNumOfBytes_() throws XmlBlasterException {
if (this.numOfBytes > -1L && !this.debug) return this.numOfBytes;
synchronized (this.modificationMonitor) {
long oldValue = this.numOfBytes;
EntryCount entryCount = this.manager.getNumOfAll(getStorageId().getStrippedId());
setEntryCount(entryCount);
//this.numOfBytes = this.manager.getNumOfBytes(getStorageId().getStrippedId());
if (this.debug) {
if (oldValue != this.numOfBytes && oldValue != -999L) { // don't log if explicitly set the oldValue
String txt = "getNumOfBytes: an inconsistency occured between the cached value and the real value of 'numOfPersistentBytes': it was '" + oldValue + "' but should have been '" + this.numOfBytes + "'";
throw new XmlBlasterException(this.glob, ErrorCode.INTERNAL_UNKNOWN, ME, txt + toXml(""));
}
}
else if (log.isLoggable(Level.FINE)) log.fine("getNumOfBytes_ old (cached) value: '" + oldValue + "' new (real) value: '" + this.numOfBytes + "'");
return this.numOfBytes;
}
}
/**
* It returns the size of the queue. Note that this call will return the size
* stored in cache.
* In case this value is -1L (which means a previous attempt to read from the
* DB failed) it will synchronize against the DB by making a call to the DB.
* If that fails it will return -1L.
*
* @see I_Queue#getNumOfBytes()
*/
public long getNumOfBytes() {
try {
return getNumOfBytes_();
}
catch (XmlBlasterException ex) {
log.fine("getNumOfBytes, exception: " + ex.getMessage());
return this.numOfBytes;
}
}
/**
* It returns the number of persistent entries in the queue.
* In case this value is -1L (which means a previous attempt to read from the
* DB failed) it will synchronize against the DB by making a call to the DB.
* If that fails it will return -1L.
*
* @param verbose If true we throw an exception on errors, if false we ignore the error silently
* @see I_Queue#getNumOfPersistentBytes()
*/
private long getNumOfPersistentBytes_(boolean verbose) throws XmlBlasterException {
if (this.numOfPersistentBytes > -1L && !this.debug) return this.numOfPersistentBytes;
synchronized (this.modificationMonitor) {
try {
long oldValue = this.numOfPersistentBytes;
EntryCount entryCount = this.manager.getNumOfAll(getStorageId().getStrippedId());
setEntryCount(entryCount);
//this.numOfPersistentBytes = this.manager.getSizeOfPersistents(getStorageId().getStrippedId());
if (this.debug) {
if (oldValue != this.numOfPersistentBytes && oldValue != -999L) { // don't log if explicitly set the oldValue
String txt = "getNumOfPersistentBytes: an inconsistency occured between the cached value and the real value of 'numOfPersistentBytes': it was '" + oldValue + "' but should have been '" + this.numOfPersistentBytes + "'";
throw new XmlBlasterException(this.glob, ErrorCode.INTERNAL_UNKNOWN, ME, txt + toXml(""));
}
}
else if (log.isLoggable(Level.FINE)) log.warning("getNumOfPersistentBytes_ old (cached) value: '" + oldValue + "' new (real) value: '" + this.numOfPersistentBytes + "'");
return this.numOfPersistentBytes;
}
catch (XmlBlasterException ex) {
if (verbose) { // If called from toXml() we need to suppress this exeption because we here call toXml() again
throw ex; // probably verbose is not needed anymore ...
}
return -1L;
}
}
}
/**
* It returns the number of persistent entries in the queue.
* In case this value is -1L (which means a previous attempt to read from the
* DB failed) it will synchronize against the DB by making a call to the DB.
* If that fails it will return -1L.
*
* @see I_Queue#getNumOfPersistentBytes()
*/
public long getNumOfPersistentBytes() {
try {
return getNumOfPersistentBytes_(true);
}
catch (XmlBlasterException ex) {
log.severe("getNumOfPersistentBytes, exception: " + ex.getMessage());
return this.numOfPersistentBytes;
}
}
/**
* @see I_Queue#getMaxNumOfBytes()
*/
public long getMaxNumOfBytes() {
return this.property.getMaxBytes();
}
/**
* 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");
}
public CommonTableDatabaseAccessor getManager() {
return this.manager;
}
/**
* Clears everything and removes the queue (i.e. frees the associated table)
*/
public long clear() {
try {
ReturnDataHolder ret = this.manager.deleteFirstEntries(getStorageId().getStrippedId(), -1, -1L);
this.numOfEntries = 0L;
this.numOfBytes = 0L;
this.numOfPersistentEntries = 0L;
this.numOfPersistentBytes = 0L;
this.storageSizeListenerHelper.invokeStorageSizeListener();
return ret.countEntries;
}
catch (XmlBlasterException ex) {
log.severe("exception: " + ex.getMessage());
return 0;
}
}
/**
* @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 yet");
}
/**
* Shutdown the implementation, sync with data store
*/
public void shutdown() {
synchronized (this) {
if (log.isLoggable(Level.FINER)) log.finer("shutdown '" + this.storageId + "' (currently the value of 'isDown' is '" + this.isDown + "'");
if (this.isDown) return;
this.isDown = true;
}
this.storageSizeListenerHelper.invokeStorageSizeListener();
this.removeStorageSizeListener(null);
synchronized (this) {
this.manager.unregisterQueue(this);
}
glob.getQueuePluginManager().cleanup(this);
if (glob instanceof ServerScope) {
((ServerScope)glob).getStoragePluginManager().cleanup(this);
}
}
public boolean isShutdown() {
return this.isDown;
}
/**
* @return a human readable usage help string
*/
public String usage() {
return "no usage";
}
/**
* @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;
// NOTE: Recursion problems when using getNumOfEntries() instead of this.numOfEntries
// if an exception is thrown in getNumOfEntries() which uses toXml to dump the problem ...
sb.append(offset).append("<JdbcQueueCommonTablePlugin id='").append(getStorageId().getId());
sb.append("' type='").append(getType());
sb.append("' version='").append(getVersion());
sb.append("' numOfEntries='").append(this.numOfEntries);
sb.append("' numOfBytes='").append(this.numOfBytes);
sb.append("'>");
if (this.property != null) {
sb.append(this.property.toXml(extraOffset+Constants.INDENT));
}
else {
sb.append(offset).append("<isDown>").append(this.isDown).append("</isDown>");
}
try {
sb.append(offset).append(" <numOfPersistentsCached>").append(this.numOfPersistentEntries).append("</numOfPersistentsCached>");
sb.append(offset).append(" <sizeOfPersistentsCached>").append(getNumOfPersistentBytes()).append("</sizeOfPersistentsCached>");
sb.append(offset).append(" <numOfEntriesCached>").append(this.numOfEntries).append("</numOfEntriesCached>");
sb.append(offset).append(" <numOfBytesCached>").append(getNumOfBytes()).append("</numOfBytesCached>");
sb.append(offset).append(" <numOfEntries>").append(getNumOfEntries_()).append("</numOfEntries>");
sb.append(offset).append(" <numOfBytes>").append(getNumOfBytes_()).append("</numOfBytes>");
sb.append(offset).append(" <numOfPersistents>").append(getNumOfPersistentEntries_(false)).append("</numOfPersistents>");
sb.append(offset).append(" <sizeOfPersistents>").append(getNumOfPersistentBytes_(false)).append("</sizeOfPersistents>");
}
catch (XmlBlasterException e) {
}
sb.append(offset).append("</JdbcQueueCommonTablePlugin>");
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;
}
/**
* Enforced by I_Plugin
* @return "JDBC"
*/
public String getType() { return "JDBC"; }
/**
* 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; }
/**
* Cleans up the current queue (it deletes all the entries and frees the
* table used and cleans up all tables in the database). This method should
* never be called, only for testing purposes or to clean up the entire
* database.
*/
public void destroy() throws XmlBlasterException {
this.clear();
this.shutdown();
this.property = null;
this.manager.cleanUp(this.storageId.getStrippedId());
}
/////////////////////////// I_Map implementation ///////////////////////
public I_MapEntry get(final long uniqueId) throws XmlBlasterException {
long[] idArr = new long[] { uniqueId };
ArrayList list = this.manager.getEntries(getStorageId(), idArr);
if (list.size() < 1) {
return null;
}
return (I_MapEntry)list.get(0);
}
/**
* @see I_Map#getAll()
*/
public I_MapEntry[] getAll(I_EntryFilter entryFilter) throws XmlBlasterException {
ArrayList list = this.manager.getEntries(getStorageId(), -1, -1L, entryFilter);
return (I_MapEntry[])list.toArray(new I_MapEntry[list.size()]);
}
/**
* @see I_Map#put(I_MapEntry)
*/
public int put(I_MapEntry mapEntry) throws XmlBlasterException {
if (mapEntry == null) {
throw new XmlBlasterException(glob, ErrorCode.INTERNAL_ILLEGALARGUMENT, ME, "put(I_MapEntry="+mapEntry+")");
}
synchronized (this.modificationMonitor) {
if (put((I_Entry)mapEntry))
return 1;
return 0;
}
}
public int remove(final I_MapEntry mapEntry) throws XmlBlasterException {
int num = removeRandom(mapEntry);
synchronized (this.modificationMonitor) {
}
return num;
}
public int remove(final long uniqueId) throws XmlBlasterException {
int num = removeRandom(uniqueId);
synchronized (this.modificationMonitor) {
}
return num;
}
/**
* @see I_Map#removeOldest()
*/
public I_MapEntry removeOldest() throws XmlBlasterException {
throw new XmlBlasterException(glob, ErrorCode.INTERNAL_NOTIMPLEMENTED, ME, "removeOldest is not implemented");
}
/**
* @see org.xmlBlaster.util.queue.I_StorageProblemNotifier#registerStorageProblemListener(I_StorageProblemListener)
*/
public boolean registerStorageProblemListener(I_StorageProblemListener listener) {
if (this.manager == null) return false;
return this.manager.registerStorageProblemListener(listener);
}
/**
* @see org.xmlBlaster.util.queue.I_StorageProblemNotifier#unRegisterStorageProblemListener(I_StorageProblemListener)
*/
public boolean unRegisterStorageProblemListener(I_StorageProblemListener listener) {
if (this.manager == null || listener == null) return false;
return this.manager.unRegisterStorageProblemListener(listener);
}
/**
* @see I_Map#change(I_MapEntry, I_ChangeCallback)
*/
public I_MapEntry change(I_MapEntry entry, I_ChangeCallback callback) throws XmlBlasterException {
synchronized(this) { // is this the correct synchronization ??
entry.getSizeInBytes(); // must be here since newEntry could reference same obj.
I_MapEntry newEntry = entry;
if (callback != null) newEntry = callback.changeEntry(entry);
if (newEntry == null) return entry;
if (newEntry.isPersistent() != entry.isPersistent()) {
throw new XmlBlasterException(this.glob, ErrorCode.INTERNAL_UNKNOWN, ME + ".change",
"changing oldEntry.isPersistent=" + entry.isPersistent() + " to newEntry.isPersistent=" + newEntry.isPersistent() + "differs. This is not allowed");
}
long diffSize = this.manager.modifyEntry(getStorageId().getStrippedId(), newEntry, entry);
this.numOfBytes += diffSize;
if (entry.isPersistent())
this.numOfPersistentBytes += diffSize;
return newEntry;
}
}
/**
* @see I_Map#change(I_MapEntry, I_ChangeCallback)
*/
public void updateCounters(I_MapEntry entry) throws XmlBlasterException {
change(entry, null);
}
/**
* @see I_Map#change(long, I_ChangeCallback)
*/
public I_MapEntry change(long uniqueId, I_ChangeCallback callback) throws XmlBlasterException {
synchronized (this) {
I_MapEntry oldEntry = get(uniqueId);
return change(oldEntry, callback);
}
}
/**
* @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_Map#embeddedObjectsToXml(OutputStream, Properties)
*/
public long embeddedObjectsToXml(final OutputStream out, final Properties props) throws Exception {
if (out == null) return 0;
entryCounter = 0;
/*I_Entry[] results = */getAll(new I_EntryFilter() {
public I_Entry intercept(I_Entry entry, I_Storage storage) {
entryCounter++;
try {
entry.embeddedObjectToXml(out, props);
}
catch (IOException e) {
log.warning("Ignoring dumpToFile() problem: "+e.toString());
}
return null;
}
});
return entryCounter;
}
}