[jboss-cvs] JBossCache/src/org/jboss/cache/pojo/impl ...
Jason Thomas Greene
jgreene at jboss.com
Fri Jun 29 00:34:01 EDT 2007
User: jgreene
Date: 07/06/29 00:34:01
Modified: src/org/jboss/cache/pojo/impl CacheListenerAdaptor.java
NotificationDispatcher.java PojoCacheImpl.java
Log:
Implement new POJO Annotation Notification API
Revision Changes Path
1.7 +92 -38 JBossCache/src/org/jboss/cache/pojo/impl/CacheListenerAdaptor.java
(In the diff below, changes in quantity of whitespace are not shown.)
Index: CacheListenerAdaptor.java
===================================================================
RCS file: /cvsroot/jboss/JBossCache/src/org/jboss/cache/pojo/impl/CacheListenerAdaptor.java,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -b -r1.6 -r1.7
--- CacheListenerAdaptor.java 28 Jun 2007 16:53:39 -0000 1.6
+++ CacheListenerAdaptor.java 29 Jun 2007 04:34:01 -0000 1.7
@@ -22,23 +22,10 @@
package org.jboss.cache.pojo.impl;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.jboss.cache.Fqn;
-import static org.jboss.cache.notifications.event.NodeModifiedEvent.ModificationType;
-import org.jboss.cache.pojo.PojoCache;
-import org.jboss.cache.pojo.PojoCacheListener;
import static org.jboss.cache.pojo.impl.InternalConstant.POJOCACHE_OPERATION;
import static org.jboss.cache.pojo.impl.InternalConstant.POJOCACHE_STATUS;
-import org.jboss.cache.pojo.interceptors.PojoTxLockInterceptor;
-import org.jboss.cache.pojo.notification.AttachNotification;
-import org.jboss.cache.pojo.notification.DetachNotification;
-import org.jboss.cache.pojo.notification.FieldModifyNotification;
-import org.jboss.cache.pojo.notification.ListModifyNotification;
-import org.jboss.cache.pojo.notification.MapModifyNotification;
-import org.jboss.cache.pojo.notification.Notification;
-import org.jboss.cache.pojo.notification.NotificationContext;
-import org.jboss.cache.pojo.notification.SetModifyNotification;
+import static org.jboss.cache.notifications.event.Event.Type.TRANSACTION_COMPLETED;
+import static org.jboss.cache.notifications.event.Event.Type.TRANSACTION_REGISTERED;
import java.lang.reflect.Field;
import java.util.Collection;
@@ -48,14 +35,40 @@
import java.util.Set;
import java.util.regex.Pattern;
-// $Id: CacheListenerAdaptor.java,v 1.6 2007/06/28 16:53:39 msurtani Exp $
+import javax.transaction.Transaction;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.notifications.annotation.CacheListener;
+import org.jboss.cache.notifications.annotation.NodeModified;
+import org.jboss.cache.notifications.annotation.TransactionCompleted;
+import org.jboss.cache.notifications.annotation.TransactionRegistered;
+import org.jboss.cache.notifications.event.NodeModifiedEvent;
+import org.jboss.cache.notifications.event.TransactionalEvent;
+import org.jboss.cache.notifications.event.NodeModifiedEvent.ModificationType;
+import org.jboss.cache.pojo.PojoCache;
+import org.jboss.cache.pojo.interceptors.PojoTxLockInterceptor;
+import org.jboss.cache.pojo.notification.NotificationContext;
+import org.jboss.cache.pojo.notification.event.AttachedEvent;
+import org.jboss.cache.pojo.notification.event.DetachedEvent;
+import org.jboss.cache.pojo.notification.event.Event;
+import org.jboss.cache.pojo.notification.event.FieldModifiedEvent;
+import org.jboss.cache.pojo.notification.event.ListModifiedEvent;
+import org.jboss.cache.pojo.notification.event.MapModifiedEvent;
+import org.jboss.cache.pojo.notification.event.SetModifiedEvent;
+import org.jboss.cache.pojo.notification.event.TransactionCompletedEvent;
+import org.jboss.cache.pojo.notification.event.TransactionRegisteredEvent;
+
+// $Id: CacheListenerAdaptor.java,v 1.7 2007/06/29 04:34:01 jgreene Exp $
/**
* Adapts the core cache listener API into the POJO listener API.
*
* @author Jason T. Greene
*/
-public class CacheListenerAdaptor implements NotificationContext
+ at CacheListener
+public class CacheListenerAdaptor
{
private static final HashSet<String> internalKeys = new HashSet<String>();
private static final Log log = LogFactory.getLog(CacheListenerAdaptor.class);
@@ -82,7 +95,7 @@
return this.cache;
}
- private FieldModifyNotification createModifyEvent(Fqn fqn, String key, Object value, boolean local)
+ private FieldModifiedEvent createModifyEvent(NotificationContext context, Fqn fqn, String key, Object value, boolean local)
{
if (value instanceof PojoReference)
value = cache.find(((PojoReference) value).getFqn().toString());
@@ -100,10 +113,27 @@
return null;
}
- return new FieldModifyNotification(this, o, f, value, local);
+ return new FieldModifiedEvent(context, o, f, value, local);
+ }
+
+ private NotificationContext createContext(final TransactionalEvent event)
+ {
+ return new NotificationContext()
+ {
+
+ public PojoCache getPojoCache()
+ {
+ return CacheListenerAdaptor.this.getPojoCache();
+ }
+
+ public Transaction getTransaction()
+ {
+ return event.getTransaction();
+ }
+ };
}
- private void sendNotification(Notification notification, Set<PojoCacheListener> listeners)
+ private void sendNotification(Event notification, Set<NotificationDispatcher.Entry> listeners)
{
if (listeners == null)
dispatcher.dispatch(notification);
@@ -111,13 +141,37 @@
dispatcher.dispatch(notification, listeners);
}
- public void nodeModified(Fqn fqn, boolean pre, boolean isLocal, ModificationType modType, Map<Object, Object> data)
+ @TransactionCompleted
+ @TransactionRegistered
+ public void handleTransaction(TransactionalEvent event)
+ {
+ if (event.getType() == TRANSACTION_COMPLETED)
{
+ boolean successful = ((org.jboss.cache.notifications.event.TransactionCompletedEvent) event).isSuccessful();
+ sendNotification(new TransactionCompletedEvent(createContext(event), successful, event.isOriginLocal()), null);
+ }
+ else if (event.getType() == TRANSACTION_REGISTERED)
+ {
+ sendNotification(new TransactionRegisteredEvent(createContext(event), event.isOriginLocal()), null);
+ }
+ }
+
+ @NodeModified
+ public void nodeModified(NodeModifiedEvent event)
+ {
+ Map<Object, Object> data = event.getData();
+ boolean pre = event.isPre();
+ Fqn fqn = event.getFqn();
+ ModificationType modType = event.getModificationType();
+ boolean isLocal = event.isOriginLocal();
+
if (pre)
return;
+ //System.out.println(fqn + " " + modType + " " + data);
+
// If we are filtering, don't load as much as possible
- Set<PojoCacheListener> matched = null;
+ Set<NotificationDispatcher.Entry> matched = null;
if (dispatcher.hasFilters())
{
matched = matchListeners(fqn);
@@ -130,12 +184,12 @@
if ("ATTACHED".equals(data.get(POJOCACHE_STATUS)))
{
Object o = cache.find(fqn.toString());
- sendNotification(new AttachNotification(this, o, isLocal), matched);
+ sendNotification(new AttachedEvent(createContext(event), o, isLocal), matched);
}
else if ("DETACHING".equals(data.get(POJOCACHE_STATUS)))
{
Object o = cache.find(fqn.toString());
- sendNotification(new DetachNotification(this, o, isLocal), matched);
+ sendNotification(new DetachedEvent(createContext(event), o, isLocal), matched);
}
else if (data.containsKey(POJOCACHE_OPERATION))
{
@@ -143,21 +197,21 @@
if (collection instanceof List)
{
int i = Integer.parseInt(fqn.getLastElementAsString());
- ListModifyNotification.Operation operation = ListModifyNotification.Operation.valueOf(data.get(POJOCACHE_OPERATION).toString());
+ ListModifiedEvent.Operation operation = ListModifiedEvent.Operation.valueOf(data.get(POJOCACHE_OPERATION).toString());
Object value = cache.find(fqn.toString());
- sendNotification(new ListModifyNotification(this, (List) collection, operation, i, value, isLocal), matched);
+ sendNotification(new ListModifiedEvent(createContext(event), (List) collection, operation, i, value, isLocal), matched);
}
else if (collection instanceof Set)
{
- SetModifyNotification.Operation operation = SetModifyNotification.Operation.valueOf(data.get(POJOCACHE_OPERATION).toString());
+ SetModifiedEvent.Operation operation = SetModifiedEvent.Operation.valueOf(data.get(POJOCACHE_OPERATION).toString());
Object value = cache.find(fqn.toString());
- sendNotification(new SetModifyNotification(this, (Set) collection, operation, value, isLocal), matched);
+ sendNotification(new SetModifiedEvent(createContext(event), (Set) collection, operation, value, isLocal), matched);
}
else if (collection instanceof Map)
{
- MapModifyNotification.Operation operation = MapModifyNotification.Operation.valueOf(data.get(POJOCACHE_OPERATION).toString());
+ MapModifiedEvent.Operation operation = MapModifiedEvent.Operation.valueOf(data.get(POJOCACHE_OPERATION).toString());
Object value = cache.find(fqn.toString());
- sendNotification(new MapModifyNotification(this, (Map) collection, operation, fqn.getLastElement(), value, isLocal), matched);
+ sendNotification(new MapModifiedEvent(createContext(event), (Map) collection, operation, fqn.getLastElement(), value, isLocal), matched);
}
}
else if ("ATTACHED".equals(cache.getCache().get(fqn, POJOCACHE_STATUS)))
@@ -170,7 +224,7 @@
if (internalKeys.contains(key))
continue;
- sendNotification(createModifyEvent(fqn, key, value, isLocal), matched);
+ sendNotification(createModifyEvent(createContext(event), fqn, key, value, isLocal), matched);
}
}
}
@@ -182,16 +236,16 @@
if (internalKeys.contains(key))
continue;
- sendNotification(createModifyEvent(fqn, key, null, isLocal), matched);
+ sendNotification(createModifyEvent(createContext(event), fqn, key, null, isLocal), matched);
}
}
}
- private Set<PojoCacheListener> matchListeners(Fqn fqn)
+ private Set<NotificationDispatcher.Entry> matchListeners(Fqn fqn)
{
PojoInstance instance = (PojoInstance) cache.getCache().get(fqn, PojoInstance.KEY);
if (instance != null)
- return dispatcher.getListeners(instance.getReferences());
+ return dispatcher.getListenerEntries(instance.getReferences());
return null;
}
@@ -201,17 +255,17 @@
return dispatcher.isEmpty();
}
- public Collection<PojoCacheListener> getListeners()
+ public Collection<Object> getListeners()
{
return dispatcher.getListeners();
}
- public void addListener(PojoCacheListener listener)
+ public void addListener(Object listener)
{
dispatcher.add(listener);
}
- public void addListener(PojoCacheListener listener, Pattern pattern)
+ public void addListener(Object listener, Pattern pattern)
{
if (pattern == null)
dispatcher.add(listener);
@@ -219,7 +273,7 @@
dispatcher.add(listener, pattern);
}
- public void removeListener(PojoCacheListener listener)
+ public void removeListener(Object listener)
{
dispatcher.remove(listener);
}
1.2 +139 -68 JBossCache/src/org/jboss/cache/pojo/impl/NotificationDispatcher.java
(In the diff below, changes in quantity of whitespace are not shown.)
Index: NotificationDispatcher.java
===================================================================
RCS file: /cvsroot/jboss/JBossCache/src/org/jboss/cache/pojo/impl/NotificationDispatcher.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -b -r1.1 -r1.2
--- NotificationDispatcher.java 30 May 2007 06:08:01 -0000 1.1
+++ NotificationDispatcher.java 29 Jun 2007 04:34:01 -0000 1.2
@@ -22,7 +22,10 @@
package org.jboss.cache.pojo.impl;
+import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -30,53 +33,88 @@
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
import org.jboss.cache.Fqn;
import org.jboss.cache.pojo.PojoCacheException;
-import org.jboss.cache.pojo.PojoCacheListener;
-import org.jboss.cache.pojo.notification.Notification;
+import org.jboss.cache.pojo.notification.annotation.ArrayModified;
+import org.jboss.cache.pojo.notification.annotation.Attached;
+import org.jboss.cache.pojo.notification.annotation.Detached;
+import org.jboss.cache.pojo.notification.annotation.FieldModified;
+import org.jboss.cache.pojo.notification.annotation.ListModified;
+import org.jboss.cache.pojo.notification.annotation.MapModified;
+import org.jboss.cache.pojo.notification.annotation.PojoCacheListener;
+import org.jboss.cache.pojo.notification.annotation.SetModified;
+import org.jboss.cache.pojo.notification.annotation.TransactionCompleted;
+import org.jboss.cache.pojo.notification.annotation.TransactionRegistered;
+import org.jboss.cache.pojo.notification.event.ArrayModifiedEvent;
+import org.jboss.cache.pojo.notification.event.AttachedEvent;
+import org.jboss.cache.pojo.notification.event.DetachedEvent;
+import org.jboss.cache.pojo.notification.event.Event;
+import org.jboss.cache.pojo.notification.event.FieldModifiedEvent;
+import org.jboss.cache.pojo.notification.event.ListModifiedEvent;
+import org.jboss.cache.pojo.notification.event.MapModifiedEvent;
+import org.jboss.cache.pojo.notification.event.SetModifiedEvent;
+import org.jboss.cache.pojo.notification.event.TransactionCompletedEvent;
+import org.jboss.cache.pojo.notification.event.TransactionRegisteredEvent;
+
+// $Id: NotificationDispatcher.java,v 1.2 2007/06/29 04:34:01 jgreene Exp $
/**
- * Dispatches notificaiton events to POJO cache listeners.
+ * Dispatches notification events to POJO cache listeners.
*
* @author Jason T. Greene
- * @revision $Id: NotificationDispatcher.java,v 1.1 2007/05/30 06:08:01 jgreene Exp $
+ * @revision $Id: NotificationDispatcher.java,v 1.2 2007/06/29 04:34:01 jgreene Exp $
*/
-public class NotificationDispatcher
+class NotificationDispatcher
{
- private final static Map<Class<?>, Method> notifiers = new HashMap<Class<?>, Method>();
+ private final static Map<Class<? extends Annotation>, Class<? extends Event>> annotations = new HashMap<Class<? extends Annotation>, Class<? extends Event>>();
private final Set<Entry> listeners = new CopyOnWriteArraySet<Entry>();
- private Set<PojoCacheListener> filteredListeners = new HashSet<PojoCacheListener>();
+ private Set<Object> filteredListeners = new HashSet<Object>();
private volatile boolean hasFilters;
- private final static class Entry
+ static
{
- private final PojoCacheListener listener;
+ annotations.put(Attached.class, AttachedEvent.class);
+ annotations.put(Detached.class, DetachedEvent.class);
+ annotations.put(FieldModified.class, FieldModifiedEvent.class);
+ annotations.put(ListModified.class, ListModifiedEvent.class);
+ annotations.put(MapModified.class, MapModifiedEvent.class);
+ annotations.put(SetModified.class, SetModifiedEvent.class);
+ annotations.put(ArrayModified.class, ArrayModifiedEvent.class);
+ annotations.put(TransactionRegistered.class, TransactionRegisteredEvent.class);
+ annotations.put(TransactionCompleted.class, TransactionCompletedEvent.class);
+ }
+
+ final static class Entry
+ {
+ private final Object listener;
private final Pattern pattern;
+ private final Map<Class<?>, List<Method>> notifiers;
- public Entry(PojoCacheListener listener)
+ private Entry(Object listener, boolean build)
{
- this(listener, null);
+ this(listener, build, null);
}
- public Entry(PojoCacheListener listener, Pattern pattern)
+ private Entry(Object listener, boolean build, Pattern pattern)
{
if (listener == null)
throw new IllegalArgumentException("Listener can't be null");
this.listener = listener;
this.pattern = pattern;
+
+ this.notifiers = build ? buildNotifiers(listener.getClass()) : null;
}
-// pattern is just extra information, equality is confined to listener
+ // equality is confined to listener
public int hashCode()
{
return listener.hashCode();
}
- // pattern is just extra information, equality is confined to listener
+ // equality is confined to listener
public boolean equals(Object o)
{
if (o == this)
@@ -86,62 +124,93 @@
return ((Entry)o).equals(listener);
}
- }
- static
+ private static Map<Class<?>, List<Method>> buildNotifiers(Class clazz)
+ {
+ if (! Modifier.isPublic(clazz.getModifiers()))
+ throw new IllegalArgumentException("Listener must be public! Class:" + clazz.getName());
+
+ if (! clazz.isAnnotationPresent(PojoCacheListener.class))
+ throw new IllegalArgumentException("Not a listener, class did not contain @PojoCacheListener. Class: " + clazz.getName());
+
+
+ Map<Class<?>, List<Method>> notifiers = new HashMap<Class<?>, List<Method>>();
+ for (Method method : clazz.getMethods())
{
- for (Method method : PojoCacheListener.class.getMethods())
+ for (Annotation annotation : method.getAnnotations())
{
- Class<?>[] parameterTypes = method.getParameterTypes();
- if (parameterTypes.length == 1 && Notification.class.isAssignableFrom(parameterTypes[0]))
- notifiers.put(parameterTypes[0], method);
+ Class<? extends Event> event = annotations.get(annotation.annotationType());
+ if (event == null)
+ continue;
+
+ Class<?>[] types = method.getParameterTypes();
+ if (types.length != 1 || !types[0].isAssignableFrom(event))
+ {
+ throw new IllegalArgumentException("Listener has invlaid method signature for annotation. " +
+ "Method: \"" + method.getName() + "\" " +
+ "Annotation: \"" + annotation.annotationType().getSimpleName() + "\" " +
+ "Expected Parameter: \"" + event.getSimpleName() + "\"");
+ }
+
+ List<Method> list = notifiers.get(event);
+ if (list == null)
+ {
+ list = new ArrayList<Method>();
+ notifiers.put(event, list);
+ }
+
+ list.add(method);
+ }
+ }
+
+ return notifiers;
}
}
- public void add(PojoCacheListener listener)
+ void add(Object listener)
{
- listeners.add(new Entry(listener));
+ listeners.add(new Entry(listener, true));
}
// gaurds filteredListeners
- public synchronized void add(PojoCacheListener listener, Pattern pattern)
+ synchronized void add(Object listener, Pattern pattern)
{
- listeners.add(new Entry(listener, pattern));
+ listeners.add(new Entry(listener, true, pattern));
filteredListeners.add(listener);
hasFilters = true;
}
// gaurds filteredListeners
- public synchronized void remove(PojoCacheListener listener)
+ synchronized void remove(Object listener)
{
filteredListeners.remove(listener);
if (filteredListeners.size() == 0)
hasFilters = false;
- listeners.remove(new Entry(listener));
+ listeners.remove(new Entry(listener, false));
}
- public boolean hasFilters()
+ boolean hasFilters()
{
return hasFilters;
}
- public Set<PojoCacheListener> getListeners()
+ Set<Object> getListeners()
{
- Set<PojoCacheListener> set = new HashSet<PojoCacheListener>();
+ Set<Object> set = new HashSet<Object>();
for (Entry entry : listeners)
set.add(entry.listener);
return Collections.unmodifiableSet(set);
}
- public Set<PojoCacheListener> getListeners(List<Fqn> fqns)
+ Set<Entry> getListenerEntries(List<Fqn> fqns)
{
- Set<PojoCacheListener> set = new HashSet<PojoCacheListener>();
+ Set<Entry> set = new HashSet<Entry>();
for (Entry entry : listeners)
{
if (entry.pattern == null)
{
- set.add(entry.listener);
+ set.add(entry);
continue;
}
@@ -149,7 +218,7 @@
{
if (entry.pattern.matcher(fqn.toString()).matches())
{
- set.add(entry.listener);
+ set.add(entry);
break;
}
}
@@ -158,35 +227,37 @@
return set;
}
- public boolean isEmpty()
+ boolean isEmpty()
{
return listeners.size() == 0;
}
- public void dispatch(Notification notification)
+ void dispatch(Event notification)
{
for (Entry entry : listeners)
{
// Prevent dispatch to filtered entries
if (entry.pattern == null)
- dispatch(notification, entry.listener);
+ dispatch(notification, entry);
}
}
- public void dispatch(Notification notification, Set<PojoCacheListener> listeners)
+ void dispatch(Event notification, Set<Entry> listeners)
{
- for (PojoCacheListener listener : listeners)
+ for (Entry listener : listeners)
dispatch(notification, listener);
}
- private void dispatch(Notification notification, PojoCacheListener listener)
+ private void dispatch(Event notification, Entry entry)
{
- Method method = notifiers.get(notification.getClass());
+ List<Method> methods = entry.notifiers.get(notification.getClass());
+ if (methods == null)
+ return;
try
{
- if (method != null)
- method.invoke(listener, notification);
+ for (Method method : methods)
+ method.invoke(entry.listener, notification);
}
catch (Exception e)
{
1.12 +16 -7 JBossCache/src/org/jboss/cache/pojo/impl/PojoCacheImpl.java
(In the diff below, changes in quantity of whitespace are not shown.)
Index: PojoCacheImpl.java
===================================================================
RCS file: /cvsroot/jboss/JBossCache/src/org/jboss/cache/pojo/impl/PojoCacheImpl.java,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -b -r1.11 -r1.12
--- PojoCacheImpl.java 28 Jun 2007 00:56:06 -0000 1.11
+++ PojoCacheImpl.java 29 Jun 2007 04:34:01 -0000 1.12
@@ -24,16 +24,16 @@
import org.jboss.cache.factories.XmlConfigurationParser;
import org.jboss.cache.pojo.PojoCache;
import org.jboss.cache.pojo.PojoCacheException;
-import org.jboss.cache.pojo.PojoCacheListener;
import org.jboss.cache.pojo.annotation.Attach;
import org.jboss.cache.pojo.annotation.Detach;
import org.jboss.cache.pojo.annotation.Find;
+import org.jboss.cache.pojo.notification.annotation.PojoCacheListener;
/**
* Implementation class for PojoCache interface
*
* @author Ben Wang
- * @version $Id: PojoCacheImpl.java,v 1.11 2007/06/28 00:56:06 jgreene Exp $
+ * @version $Id: PojoCacheImpl.java,v 1.12 2007/06/29 04:34:01 jgreene Exp $
*/
public class PojoCacheImpl implements PojoCache
{
@@ -273,29 +273,38 @@
cache.destroy();
}
- public Collection<PojoCacheListener> getListeners()
+ public Collection<Object> getListeners()
{
return listenerAdaptor.getListeners();
}
- public void addListener(PojoCacheListener listener)
+ public void addListener(Object listener)
{
addListener(listener, null);
}
- public void addListener(PojoCacheListener listener, Pattern pattern)
+ public void addListener(Object listener, Pattern pattern)
{
// Add and remove listner operations must be serialized to ensure that
// the adaptor is always present only once, when at least one listener
// is registered.
synchronized (listenerAdaptor)
{
+ try
+ {
listenerAdaptor.addListener(listener, pattern);
+ }
+ catch (IllegalArgumentException e)
+ {
+ // simplify stack trace for user
+ e.fillInStackTrace();
+ throw e;
+ }
cache.addCacheListener(listenerAdaptor);
}
}
- public void removeListener(PojoCacheListener listener)
+ public void removeListener(Object listener)
{
synchronized (listenerAdaptor)
{
More information about the jboss-cvs-commits
mailing list