package org.jactr.extensions.cached.procedural.listeners;
/*
* default logging
*/
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javolution.util.FastList;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jactr.core.slot.INotifyingSlotContainer;
import org.jactr.core.slot.event.ISlotContainerListener;
import org.jactr.core.slot.event.SlotEvent;
import org.jactr.extensions.cached.procedural.invalidators.IInvalidator;
import org.jactr.extensions.cached.procedural.invalidators.SlotInvalidator;
public class SlotListener implements ISlotContainerListener
{
/**
* Logger definition
*/
static private final transient Log LOGGER = LogFactory
.getLog(SlotListener.class);
private final INotifyingSlotContainer _container;
private final ConcurrentHashMap<String, FastList<IInvalidator>> _invalidators;
public SlotListener(INotifyingSlotContainer container)
{
_container = container;
_invalidators = new ConcurrentHashMap<String, FastList<IInvalidator>>();
_container.addListener(this, null);
}
/**
* return true if there are no registered invalidators. This is used to signal
* when we can detach.
*
* @return
*/
public boolean isEmpty()
{
/*
* we can mostly do this safely since it is called at the end of the model
* cycle, and there better not be any instantiation tasks on going.
*/
Iterator<Map.Entry<String, FastList<IInvalidator>>> mapItr = _invalidators
.entrySet().iterator();
while (mapItr.hasNext())
{
Map.Entry<String, FastList<IInvalidator>> entry = mapItr.next();
if (entry.getValue().size() == 0)
{
mapItr.remove();
if (LOGGER.isDebugEnabled())
LOGGER.debug(String.format("Recycle : %s used list:%d",
entry.getKey(), entry.getValue().hashCode()));
FastList.recycle(entry.getValue());
}
}
return _invalidators.size() == 0;
}
public void dispose()
{
_container.removeListener(this);
_invalidators.clear();
}
public INotifyingSlotContainer getContainer()
{
return _container;
}
public void slotAdded(SlotEvent se)
{
// noop
}
public void slotRemoved(SlotEvent se)
{
// noop
}
public void slotChanged(SlotEvent se)
{
try
{
invalidate(se.getSlot().getName());
}
catch (Exception e)
{
LOGGER.error("Failed to invalidate ", e);
}
}
public void register(SlotInvalidator invalidator)
{
FastList<IInvalidator> list = FastList.newInstance();
Collection<IInvalidator> invalidators = _invalidators.putIfAbsent(
invalidator.getSlotName().toLowerCase(), list);
// if null, there was nothing, but now list is in list in the map
if (invalidators == null) invalidators = list;
synchronized (invalidators)
{
invalidators.add(invalidator);
if (LOGGER.isDebugEnabled())
LOGGER.debug(String.format("updating for %s list:%d ",
invalidator.getSlotName(), invalidator.hashCode()));
}
//
if (list != invalidators) FastList.recycle(list);
}
public void unregister(SlotInvalidator invalidator)
{
String slotName = invalidator.getSlotName().toLowerCase();
Collection<IInvalidator> invalidators = _invalidators.get(slotName);
if (invalidators != null)
synchronized (invalidators)
{
invalidators.remove(invalidator);
if (LOGGER.isDebugEnabled())
LOGGER.debug(String.format("removed for %s list:%d ",
invalidator.getSlotName(), invalidator.hashCode()));
}
}
protected FastList<IInvalidator> getInvalidators(String slotName)
{
slotName = slotName.toLowerCase();
Collection<IInvalidator> invalidators = _invalidators.get(slotName);
FastList<IInvalidator> rtn = FastList.newInstance();
if (invalidators != null) synchronized (invalidators)
{
rtn.addAll(invalidators);
}
return rtn;
}
protected void invalidate(String slotName)
{
FastList<IInvalidator> invalidators = getInvalidators(slotName);
try
{
if (invalidators.size() == 0) return;
if (LOGGER.isDebugEnabled())
LOGGER.debug(String.format("Invalidating due to slot change for %s.%s",
_container, slotName));
for (IInvalidator validator : invalidators)
validator.invalidate();
}
finally
{
FastList.recycle(invalidators);
}
}
}