// ---------------------------------------------------------------------------
// dark-matter-data
// Copyright (c) 2012 dark-matter-data committers
// ---------------------------------------------------------------------------
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
// more details.
// You should have received a copy of the GNU Lesser General Public License along
// with this program; if not, see <http://www.gnu.org/licenses/lgpl.html>.
// ---------------------------------------------------------------------------
package org.dmd.dmp.server.servlet.base.cache;
import java.util.Collection;
import org.dmd.dmc.DmcSliceInfo;
import org.dmd.dmp.server.extended.DMPEvent;
import org.dmd.dmp.server.servlet.base.interfaces.DmpEventHandlerIF;
import org.dmd.dmw.DmwNamedObjectWrapper;
import org.slf4j.LoggerFactory;
/**
* The CacheListener is a base class for the implementation of listeners for events
* that are generated by the cache i.e. events that indicate object creation, deletion
* or modification.
*/
abstract public class CacheListener {
// Our unique listener ID provided by the cache
private final long listenerId;
// Our registration with the cache which provides our handle to it
protected final CacheRegistration cacheRegistration;
// The entity that will handle events from the cache
private final DmpEventHandlerIF eventHandler;
// The slice of attributes wer're interested in, or null if we're interested in all attributes
private final DmcSliceInfo sliceInfo;
protected CacheListener(CacheRegistration reg, DmpEventHandlerIF eh, DmcSliceInfo dsi){
cacheRegistration = reg;
listenerId = cacheRegistration.getCache().getNextListenerID();
eventHandler = eh;
sliceInfo = dsi;
}
/**
* @return our cache registration.
*/
public CacheRegistration getCacheRegistration(){
return(cacheRegistration);
}
public long getListenerID(){
return(listenerId);
}
/**
* The activateAndRetrieve method will register the listener with the cache and
* retrieve the set of objects for which the particular derived class of CacheListener
* was created. By tying these operations together and synchronizing on the cache
* while the listener is added (and objects returned), we guarantee that we will
* properly receive events for the set of objects that we're interested in AFTER
* we have finished sending a set of objects associated with a GetRequest.
* <p/>
* This is due to the fact that the GetRequestProcessor (which generally calls this method)
* is synchronized against itself while the objects that come back from this call are
* being sent back to the client. If events are generated by the cache while the
* GetRequestProcessor is still sending back the objects, their forwarding will be
* blocked until sending of the objects is complete.
* <p/>
* No one said that a complex, multi-user web application would be simple ;-)
* @return a set of objects from the cache.
*/
// abstract Collection<DmwNamedObjectWrapper> activateAndRetrieve();
public Collection<DmwNamedObjectWrapper> activateAndRetrieve() {
return(cacheRegistration.getCache().addListener(this));
}
/**
* This method is called by the cache when some event has occurred. It performs
* some basic checks and, if the event passes those checks, it calls the abstract
* handleEvent() method that has to be overloaded by the listener implementation.
* @param event the event to be processed.
*/
public void processCacheEvent(DMPEvent event){
// All operations against the cache should have been originated by a well known
// cache registrant whose ID will be in the event. If this isn't the case, we
// we have a problem.
if (event.getOriginatorID() == null)
throw new IllegalStateException("OriginatorID should not be null: " + event.toOIF());
// When a client performs a Create, Delete or Set operation, they can set an option
// called notifyOriginator; in some cases, you may not want to receive your own events.
// So, we check to see if the event was generated by our own cache registrant and, if
// so, we see if it wants its own events. If not, we'll just drop the event.
if ( (event.getOriginatorID() == cacheRegistration.getID()) && (!event.isNotifyOriginator()) ){
// All request messages allow you to turn on tracking and that setting is copied
// to the event so that, if required we can trace all aspects of requests/events
if (event.isTrackingEnabled())
LoggerFactory.getLogger(getClass()).debug("Dropping event because notifyOriginator is false: " + event.toOIF());
return;
}
// We slice the event if required i.e. if slice information is available, we will only
// forward the event if any of the attributes in the slice have changed or been created
DMPEvent forwardedEvent = null;
if (sliceInfo == null)
forwardedEvent = event.clone();
else
forwardedEvent = event.getSlice(sliceInfo);
if (forwardedEvent == null){
if (event.isTrackingEnabled())
LoggerFactory.getLogger(getClass()).debug("Dropping event due to slicing: " + event.toOIF());
return;
}
// All events are tagged with the unique listener associated with this cache listener.
forwardedEvent.setListenerID(listenerId);
eventHandler.handleEvent(forwardedEvent);
}
/**
* @return a string containing information that's useful for tracing.
*/
abstract public String getTraceInfo();
}