Clover coverage report -
Coverage timestamp: Thu Jul 5 2007 20:02:32 EDT
file stats: LOC: 687   Methods: 32
NCLOC: 466   Classes: 2
 
 Source file Conditionals Statements Methods TOTAL
Notifier.java 91.7% 94.6% 100% 94.6%
coverage coverage
 1    /*
 2    * JBoss, Home of Professional Open Source
 3    *
 4    * Distributable under LGPL license.
 5    * See terms of license at gnu.org.
 6    */
 7    package org.jboss.cache.notifications;
 8   
 9    import org.apache.commons.logging.Log;
 10    import org.apache.commons.logging.LogFactory;
 11    import org.jboss.cache.Cache;
 12    import org.jboss.cache.CacheException;
 13    import org.jboss.cache.CacheSPI;
 14    import org.jboss.cache.Fqn;
 15    import org.jboss.cache.InvocationContext;
 16    import org.jboss.cache.notifications.annotation.*;
 17    import org.jboss.cache.notifications.event.*;
 18    import static org.jboss.cache.notifications.event.Event.Type.*;
 19    import org.jboss.cache.util.MapCopy;
 20    import org.jgroups.View;
 21   
 22    import javax.transaction.Transaction;
 23    import java.lang.reflect.InvocationTargetException;
 24    import java.lang.reflect.Method;
 25    import java.lang.reflect.Modifier;
 26    import java.util.Collections;
 27    import java.util.HashSet;
 28    import java.util.List;
 29    import java.util.Map;
 30    import java.util.Set;
 31    import java.util.concurrent.ConcurrentHashMap;
 32    import java.util.concurrent.CopyOnWriteArrayList;
 33   
 34    /**
 35    * Helper class that handles all notifications to registered listeners.
 36    *
 37    * @author <a href="mailto:manik@jboss.org">Manik Surtani (manik@jboss.org)</a>
 38    */
 39    public class Notifier
 40    {
 41    private Cache cache;
 42   
 43    private static final Log log = LogFactory.getLog(Notifier.class);
 44   
 45    private static Class emptyMap = Collections.emptyMap().getClass();
 46    private static Class singletonMap = Collections.singletonMap(null, null).getClass();
 47    private static final Class[] allowedMethodAnnotations =
 48    {
 49    CacheStarted.class, CacheStopped.class, CacheBlocked.class, CacheUnblocked.class, NodeCreated.class, NodeRemoved.class, NodeVisited.class, NodeModified.class, NodeMoved.class,
 50    NodeActivated.class, NodePassivated.class, NodeLoaded.class, NodeEvicted.class, TransactionRegistered.class, TransactionCompleted.class, ViewChanged.class
 51    };
 52    private static final Class[] parameterTypes =
 53    {
 54    CacheStartedEvent.class, CacheStoppedEvent.class, CacheBlockedEvent.class, CacheUnblockedEvent.class, NodeCreatedEvent.class, NodeRemovedEvent.class, NodeVisitedEvent.class, NodeModifiedEvent.class, NodeMovedEvent.class,
 55    NodeActivatedEvent.class, NodePassivatedEvent.class, NodeLoadedEvent.class, NodeEvictedEvent.class, TransactionRegisteredEvent.class, TransactionCompletedEvent.class, ViewChangedEvent.class
 56    };
 57    final Map<Class, List<ListenerInvocation>> listenerInvocations = new ConcurrentHashMap<Class, List<ListenerInvocation>>();
 58   
 59  2911 public Notifier(Cache cache)
 60    {
 61  2911 this.cache = cache;
 62    }
 63   
 64    /**
 65    * Loops through all valid methods on the object passed in, and caches the relevant methods as {@link org.jboss.cache.notifications.Notifier.ListenerInvocation}
 66    * for invocation by reflection.
 67    *
 68    * @param listener object to be considered as a listener.
 69    */
 70  428 private void validateAndAddListenerInvocation(Object listener)
 71    {
 72  428 testListenerClassValidity(listener.getClass());
 73   
 74  420 boolean foundMethods = false;
 75    // now try all methods on the listener for anything that we like. Note that only PUBLIC methods are scanned.
 76  420 for (Method m : listener.getClass().getMethods())
 77    {
 78    // loop through all valid method annotations
 79  4823 for (int i = 0; i < allowedMethodAnnotations.length; i++)
 80    {
 81  77078 if (m.isAnnotationPresent(allowedMethodAnnotations[i]))
 82    {
 83  1306 testListenerMethodValidity(m, parameterTypes[i], allowedMethodAnnotations[i].getName());
 84  1300 addListenerInvocation(allowedMethodAnnotations[i], new ListenerInvocation(listener, m));
 85  1300 foundMethods = true;
 86    }
 87    }
 88    }
 89   
 90  414 if (!foundMethods && log.isWarnEnabled())
 91  2 log.warn("Attempted to register listener of class " + listener.getClass() + ", but no valid, public methods annotated with method-level event annotations found! Ignoring listener.");
 92    }
 93   
 94  428 private void testListenerClassValidity(Class listenerClass)
 95    {
 96  428 if (!listenerClass.isAnnotationPresent(CacheListener.class))
 97  1 throw new IncorrectCacheListenerException("Cache listener class MUST be annotated with org.jboss.cache.notifications.annotation.CacheListener");
 98  427 if (!Modifier.isPublic(listenerClass.getModifiers()))
 99  7 throw new IncorrectCacheListenerException("Cache listener class MUST be public!");
 100    }
 101   
 102  1306 private void testListenerMethodValidity(Method m, Class allowedParameter, String annotationName)
 103    {
 104  1306 if (m.getParameterTypes().length != 1 || !m.getParameterTypes()[0].isAssignableFrom(allowedParameter))
 105  5 throw new IncorrectCacheListenerException("Methods annotated with " + annotationName + " must accept exactly one parameter, of assignable from type " + allowedParameter.getName());
 106  1301 if (!m.getReturnType().equals(void.class))
 107  1 throw new IncorrectCacheListenerException("Methods annotated with " + annotationName + " should have a return type of void.");
 108    }
 109   
 110  1300 private void addListenerInvocation(Class annotation, ListenerInvocation li)
 111    {
 112  1300 synchronized (listenerInvocations)
 113    {
 114  1300 List<ListenerInvocation> l = listenerInvocations.get(annotation);
 115  1300 if (l == null)
 116    {
 117  1299 l = new CopyOnWriteArrayList<ListenerInvocation>();
 118  1299 listenerInvocations.put(annotation, l);
 119    }
 120  1300 l.add(li);
 121    }
 122    }
 123   
 124    /**
 125    * Adds a cache listener to the list of cache listeners registered.
 126    *
 127    * @param listener
 128    */
 129  428 public void addCacheListener(Object listener)
 130    {
 131  428 validateAndAddListenerInvocation(listener);
 132    }
 133   
 134    /**
 135    * Removes a cache listener from the list of cache listeners registered.
 136    *
 137    * @param listener
 138    */
 139  308 public void removeCacheListener(Object listener)
 140    {
 141  308 synchronized (listenerInvocations)
 142    {
 143  4928 for (Class annotation : allowedMethodAnnotations) removeListenerInvocation(annotation, listener);
 144    }
 145    }
 146   
 147  4928 private void removeListenerInvocation(Class annotation, Object listener)
 148    {
 149  4928 List<ListenerInvocation> l = listenerInvocations.get(annotation);
 150  4928 Set<Object> markedForRemoval = new HashSet<Object>();
 151  4928 if (l != null)
 152    {
 153  226 for (ListenerInvocation li : l)
 154    {
 155  226 if (listener.equals(li.target)) markedForRemoval.add(li);
 156    }
 157   
 158  226 l.removeAll(markedForRemoval);
 159   
 160  226 if (l.size() == 0) listenerInvocations.remove(annotation);
 161    }
 162    }
 163   
 164    /**
 165    * Removes all listeners from the notifier, including the evictionPolicyListener.
 166    */
 167  2779 public void removeAllCacheListeners()
 168    {
 169  2783 synchronized (listenerInvocations)
 170    {
 171  2783 listenerInvocations.clear();
 172    }
 173    }
 174   
 175    /**
 176    * @return Retrieves an (unmodifiable) set of cache listeners registered.
 177    */
 178  19 public Set<Object> getCacheListeners()
 179    {
 180  19 Set<Object> s = new HashSet<Object>();
 181  19 synchronized (listenerInvocations)
 182    {
 183  19 for (Class annotation : allowedMethodAnnotations)
 184    {
 185  304 List<ListenerInvocation> l = listenerInvocations.get(annotation);
 186  304 if (l != null)
 187    {
 188  10 for (ListenerInvocation li : l) s.add(li.target);
 189    }
 190    }
 191    }
 192  19 return Collections.unmodifiableSet(s);
 193    }
 194   
 195    /**
 196    * Notifies all registered listeners of a nodeCreated event.
 197    *
 198    * @param fqn
 199    * @param pre
 200    * @param ctx context of invocation
 201    */
 202  357251 public void notifyNodeCreated(Fqn fqn, boolean pre, InvocationContext ctx)
 203    {
 204  357251 boolean originLocal = ctx.isOriginLocal();
 205  357251 Transaction tx = ctx.getTransaction();
 206   
 207  357251 List<ListenerInvocation> listeners = listenerInvocations.get(NodeCreated.class);
 208   
 209  357251 if (listeners != null && listeners.size() > 0)
 210    {
 211  1089 InvocationContext backup = resetInvocationContext(ctx);
 212  1089 EventImpl e = new EventImpl();
 213  1089 e.setCache(cache);
 214  1089 e.setOriginLocal(originLocal);
 215  1089 e.setPre(pre);
 216  1089 e.setFqn(fqn);
 217  1089 e.setTransaction(tx);
 218  1089 e.setType(NODE_CREATED);
 219  1089 for (ListenerInvocation listener : listeners) listener.invoke(e);
 220  1088 restoreInvocationContext(backup);
 221    }
 222    }
 223   
 224    /**
 225    * Notifies all registered listeners of a nodeModified event.
 226    *
 227    * @param fqn
 228    * @param pre
 229    * @param modificationType
 230    * @param data
 231    * @param ctx context of invocation
 232    */
 233  848956 public void notifyNodeModified(Fqn fqn, boolean pre, NodeModifiedEvent.ModificationType modificationType, Map data, InvocationContext ctx)
 234    {
 235  848956 boolean originLocal = ctx.isOriginLocal();
 236  848956 Map dataCopy = copy(data);
 237  848956 Transaction tx = ctx.getTransaction();
 238   
 239  848956 List<ListenerInvocation> listeners = listenerInvocations.get(NodeModified.class);
 240   
 241  848956 if (listeners != null && listeners.size() > 0)
 242    {
 243  1887 InvocationContext backup = resetInvocationContext(ctx);
 244  1887 EventImpl e = new EventImpl();
 245  1887 e.setCache(cache);
 246  1887 e.setOriginLocal(originLocal);
 247  1887 e.setPre(pre);
 248  1887 e.setFqn(fqn);
 249  1887 e.setTransaction(tx);
 250  1887 e.setModificationType(modificationType);
 251  1887 e.setData(dataCopy);
 252  1887 e.setType(NODE_MODIFIED);
 253  1887 for (ListenerInvocation listener : listeners) listener.invoke(e);
 254  1887 restoreInvocationContext(backup);
 255    }
 256    }
 257   
 258    /**
 259    * Notifies all registered listeners of a nodeRemoved event.
 260    *
 261    * @param fqn
 262    * @param pre
 263    * @param data
 264    * @param ctx context of invocation
 265    */
 266  28610 public void notifyNodeRemoved(Fqn fqn, boolean pre, Map data, InvocationContext ctx)
 267    {
 268  28610 boolean originLocal = ctx.isOriginLocal();
 269  28610 Map dataCopy = copy(data);
 270  28610 Transaction tx = ctx.getTransaction();
 271   
 272  28610 List<ListenerInvocation> listeners = listenerInvocations.get(NodeRemoved.class);
 273   
 274  28610 if (listeners != null && listeners.size() > 0)
 275    {
 276  48 InvocationContext backup = resetInvocationContext(ctx);
 277  48 EventImpl e = new EventImpl();
 278  48 e.setCache(cache);
 279  48 e.setOriginLocal(originLocal);
 280  48 e.setPre(pre);
 281  48 e.setFqn(fqn);
 282  48 e.setTransaction(tx);
 283  48 e.setData(dataCopy);
 284  48 e.setType(NODE_REMOVED);
 285  48 for (ListenerInvocation listener : listeners) listener.invoke(e);
 286  48 restoreInvocationContext(backup);
 287    }
 288    }
 289   
 290    /**
 291    * Notifies all registered listeners of a nodeVisited event.
 292    *
 293    * @param fqn
 294    * @param pre
 295    * @param ctx context of invocation
 296    */
 297  2991817 public void notifyNodeVisited(Fqn fqn, boolean pre, InvocationContext ctx)
 298    {
 299  2991817 Transaction tx = ctx.getTransaction();
 300   
 301  2991817 List<ListenerInvocation> listeners = listenerInvocations.get(NodeVisited.class);
 302   
 303  2991817 if (listeners != null && listeners.size() > 0)
 304    {
 305  138 InvocationContext backup = resetInvocationContext(ctx);
 306  138 EventImpl e = new EventImpl();
 307  138 e.setCache(cache);
 308  138 e.setPre(pre);
 309  138 e.setFqn(fqn);
 310  138 e.setTransaction(tx);
 311  138 e.setType(NODE_VISITED);
 312  138 for (ListenerInvocation listener : listeners) listener.invoke(e);
 313  138 restoreInvocationContext(backup);
 314    }
 315    }
 316   
 317  158 public void notifyNodeMoved(Fqn originalFqn, Fqn newFqn, boolean pre, InvocationContext ctx)
 318    {
 319  158 boolean originLocal = ctx.isOriginLocal();
 320  158 Transaction tx = ctx.getTransaction();
 321   
 322  158 List<ListenerInvocation> listeners = listenerInvocations.get(NodeMoved.class);
 323   
 324  158 if (listeners != null && listeners.size() > 0)
 325    {
 326  40 InvocationContext backup = resetInvocationContext(ctx);
 327  40 EventImpl e = new EventImpl();
 328  40 e.setCache(cache);
 329  40 e.setOriginLocal(originLocal);
 330  40 e.setPre(pre);
 331  40 e.setFqn(originalFqn);
 332  40 e.setTargetFqn(newFqn);
 333  40 e.setTransaction(tx);
 334  40 e.setType(NODE_MOVED);
 335  40 for (ListenerInvocation listener : listeners) listener.invoke(e);
 336  40 restoreInvocationContext(backup);
 337    }
 338    }
 339   
 340   
 341    /**
 342    * Notifies all registered listeners of a nodeEvicted event.
 343    *
 344    * @param fqn
 345    * @param pre
 346    * @param ctx context of invocation
 347    */
 348  175870 public void notifyNodeEvicted(final Fqn fqn, final boolean pre, InvocationContext ctx)
 349    {
 350  175870 final boolean originLocal = ctx.isOriginLocal();
 351  175870 Transaction tx = ctx.getTransaction();
 352   
 353  175870 List<ListenerInvocation> listeners = listenerInvocations.get(NodeEvicted.class);
 354   
 355  175870 if (listeners != null && listeners.size() > 0)
 356    {
 357  102 InvocationContext backup = resetInvocationContext(ctx);
 358  102 EventImpl e = new EventImpl();
 359  102 e.setCache(cache);
 360  102 e.setOriginLocal(originLocal);
 361  102 e.setPre(pre);
 362  102 e.setFqn(fqn);
 363  102 e.setTransaction(tx);
 364  102 e.setType(NODE_EVICTED);
 365  102 for (ListenerInvocation listener : listeners) listener.invoke(e);
 366  102 restoreInvocationContext(backup);
 367    }
 368    }
 369   
 370    /**
 371    * Notifies all registered listeners of a nodeLoaded event.
 372    *
 373    * @param fqn
 374    * @param pre
 375    * @param data
 376    * @param ctx context of invocation
 377    */
 378  4448 public void notifyNodeLoaded(Fqn fqn, boolean pre, Map data, InvocationContext ctx)
 379    {
 380  4448 boolean originLocal = ctx.isOriginLocal();
 381  4448 Map dataCopy = copy(data);
 382  4448 Transaction tx = ctx.getTransaction();
 383   
 384  4448 List<ListenerInvocation> listeners = listenerInvocations.get(NodeLoaded.class);
 385   
 386  4448 if (listeners != null && listeners.size() > 0)
 387    {
 388  62 InvocationContext backup = resetInvocationContext(ctx);
 389  62 EventImpl e = new EventImpl();
 390  62 e.setCache(cache);
 391  62 e.setOriginLocal(originLocal);
 392  62 e.setPre(pre);
 393  62 e.setFqn(fqn);
 394  62 e.setTransaction(tx);
 395  62 e.setData(dataCopy);
 396  62 e.setType(NODE_LOADED);
 397  62 for (ListenerInvocation listener : listeners) listener.invoke(e);
 398  62 restoreInvocationContext(backup);
 399    }
 400    }
 401   
 402    /**
 403    * Notifies all registered listeners of a nodeActivated event.
 404    *
 405    * @param fqn
 406    * @param pre
 407    * @param data
 408    * @param ctx context of invocation
 409    */
 410  4360 public void notifyNodeActivated(Fqn fqn, boolean pre, Map data, InvocationContext ctx)
 411    {
 412  4360 boolean originLocal = ctx.isOriginLocal();
 413  4360 Map dataCopy = copy(data);
 414  4360 Transaction tx = ctx.getTransaction();
 415   
 416  4360 List<ListenerInvocation> listeners = listenerInvocations.get(NodeActivated.class);
 417   
 418  4360 if (listeners != null && listeners.size() > 0)
 419    {
 420  228 InvocationContext backup = resetInvocationContext(ctx);
 421  228 EventImpl e = new EventImpl();
 422  228 e.setCache(cache);
 423  228 e.setOriginLocal(originLocal);
 424  228 e.setPre(pre);
 425  228 e.setFqn(fqn);
 426  228 e.setTransaction(tx);
 427  228 e.setData(dataCopy);
 428  228 e.setType(NODE_ACTIVATED);
 429  228 for (ListenerInvocation listener : listeners) listener.invoke(e);
 430  228 restoreInvocationContext(backup);
 431    }
 432    }
 433   
 434    /**
 435    * Notifies all registered listeners of a nodePassivated event.
 436    *
 437    * @param fqn
 438    * @param pre
 439    * @param data
 440    * @param ctx context of invocation
 441    */
 442  3122 public void notifyNodePassivated(Fqn fqn, boolean pre, Map data, InvocationContext ctx)
 443    {
 444  3122 Map dataCopy = copy(data);
 445  3122 Transaction tx = ctx.getTransaction();
 446   
 447  3122 List<ListenerInvocation> listeners = listenerInvocations.get(NodePassivated.class);
 448   
 449  3122 if (listeners != null && listeners.size() > 0)
 450    {
 451  188 InvocationContext backup = resetInvocationContext(ctx);
 452  188 EventImpl e = new EventImpl();
 453  188 e.setCache(cache);
 454  188 e.setPre(pre);
 455  188 e.setFqn(fqn);
 456  188 e.setTransaction(tx);
 457  188 e.setData(dataCopy);
 458  188 e.setType(NODE_PASSIVATED);
 459  188 for (ListenerInvocation listener : listeners) listener.invoke(e);
 460  188 restoreInvocationContext(backup);
 461    }
 462    }
 463   
 464    /**
 465    * Notifies all registered listeners of a cacheStarted event.
 466    *
 467    * @param cache cache instance to notify
 468    * @param ctx context of invocation
 469    */
 470  2811 public void notifyCacheStarted(final CacheSPI cache, InvocationContext ctx)
 471    {
 472  2811 List<ListenerInvocation> listeners = listenerInvocations.get(CacheStarted.class);
 473   
 474  2811 if (listeners != null && listeners.size() > 0)
 475    {
 476  30 InvocationContext backup = resetInvocationContext(ctx);
 477  30 EventImpl e = new EventImpl();
 478  30 e.setCache(cache);
 479  30 e.setType(CACHE_STARTED);
 480  30 for (ListenerInvocation listener : listeners) listener.invoke(e);
 481  28 restoreInvocationContext(backup);
 482    }
 483    }
 484   
 485    /**
 486    * Notifies all registered listeners of a cacheStopped event.
 487    *
 488    * @param cache cache instance to notify
 489    * @param ctx context of invocation
 490    */
 491  2781 public void notifyCacheStopped(final CacheSPI cache, InvocationContext ctx)
 492    {
 493  2785 List<ListenerInvocation> listeners = listenerInvocations.get(CacheStopped.class);
 494   
 495  2785 if (listeners != null && listeners.size() > 0)
 496    {
 497  32 InvocationContext backup = resetInvocationContext(ctx);
 498  32 EventImpl e = new EventImpl();
 499  32 e.setCache(cache);
 500  32 e.setType(CACHE_STOPPED);
 501  32 for (ListenerInvocation listener : listeners) listener.invoke(e);
 502  30 restoreInvocationContext(backup);
 503    }
 504    }
 505   
 506    /**
 507    * Notifies all registered listeners of a viewChange event. Note that viewChange notifications are ALWAYS sent
 508    * immediately.
 509    *
 510    * @param new_view
 511    * @param ctx context of invocation
 512    */
 513  2473 public void notifyViewChange(final View new_view, InvocationContext ctx)
 514    {
 515  2473 List<ListenerInvocation> listeners = listenerInvocations.get(ViewChanged.class);
 516   
 517  2473 if (listeners != null && listeners.size() > 0)
 518    {
 519  246 InvocationContext backup = resetInvocationContext(ctx);
 520  246 EventImpl e = new EventImpl();
 521  246 e.setCache(cache);
 522  246 e.setNewView(new_view);
 523  246 e.setType(VIEW_CHANGED);
 524  246 for (ListenerInvocation listener : listeners) listener.invoke(e);
 525  246 restoreInvocationContext(backup);
 526    }
 527    }
 528   
 529    /**
 530    * Notifies all registered listeners of a transaction completion event.
 531    *
 532    * @param transaction the transaction that has just completed
 533    * @param successful if true, the transaction committed. If false, this is a rollback event
 534    */
 535  1121666 public void notifyTransactionCompleted(Transaction transaction, boolean successful, InvocationContext ctx)
 536    {
 537  1121666 Transaction tx = ctx.getTransaction();
 538  1121666 boolean isOriginLocal = ctx.isOriginLocal();
 539  1121666 List<ListenerInvocation> listeners = listenerInvocations.get(TransactionCompleted.class);
 540   
 541  1121666 if (listeners != null && listeners.size() > 0)
 542    {
 543  224 InvocationContext backup = resetInvocationContext(ctx);
 544  224 EventImpl e = new EventImpl();
 545  224 e.setCache(cache);
 546  224 e.setOriginLocal(isOriginLocal);
 547  224 e.setTransaction(tx);
 548  224 e.setSuccessful(successful);
 549  224 e.setType(TRANSACTION_COMPLETED);
 550  224 for (ListenerInvocation listener : listeners) listener.invoke(e);
 551  224 restoreInvocationContext(backup);
 552    }
 553    }
 554   
 555    /**
 556    * Notifies all registered listeners of a transaction registration event.
 557    *
 558    * @param transaction the transaction that has just completed
 559    */
 560  1121827 public void notifyTransactionRegistered(Transaction transaction, InvocationContext ctx)
 561    {
 562  1121827 Transaction tx = ctx.getTransaction();
 563  1121827 boolean isOriginLocal = ctx.isOriginLocal();
 564  1121827 List<ListenerInvocation> listeners = listenerInvocations.get(TransactionRegistered.class);
 565   
 566  1121827 if (listeners != null && listeners.size() > 0)
 567    {
 568  224 InvocationContext backup = resetInvocationContext(ctx);
 569  224 EventImpl e = new EventImpl();
 570  224 e.setCache(cache);
 571  224 e.setOriginLocal(isOriginLocal);
 572  224 e.setTransaction(tx);
 573  224 e.setType(TRANSACTION_REGISTERED);
 574  224 for (ListenerInvocation listener : listeners) listener.invoke(e);
 575  224 restoreInvocationContext(backup);
 576    }
 577    }
 578   
 579  5876 public void notifyCacheBlocked(CacheSPI cache, boolean pre)
 580    {
 581  5876 List<ListenerInvocation> listeners = listenerInvocations.get(CacheBlocked.class);
 582   
 583  5876 if (listeners != null && listeners.size() > 0)
 584    {
 585  0 EventImpl e = new EventImpl();
 586  0 e.setCache(cache);
 587  0 e.setPre(pre);
 588  0 e.setType(CACHE_BLOCKED);
 589  0 for (ListenerInvocation listener : listeners) listener.invoke(e);
 590    }
 591    }
 592   
 593  5178 public void notifyCacheUnblocked(CacheSPI cache, boolean pre)
 594    {
 595  5178 List<ListenerInvocation> listeners = listenerInvocations.get(CacheUnblocked.class);
 596   
 597  5178 if (listeners != null && listeners.size() > 0)
 598    {
 599  0 EventImpl e = new EventImpl();
 600  0 e.setCache(cache);
 601  0 e.setPre(pre);
 602  0 e.setType(CACHE_UNBLOCKED);
 603  0 for (ListenerInvocation listener : listeners) listener.invoke(e);
 604    }
 605    }
 606   
 607   
 608  889496 private Map copy(Map data)
 609    {
 610  884007 if (safe(data)) return data;
 611  5489 return new MapCopy(data);
 612    }
 613   
 614  4533 private void restoreInvocationContext(InvocationContext backup)
 615    {
 616  4533 cache.setInvocationContext(backup);
 617    }
 618   
 619    /**
 620    * Resets the current (passed-in) invocation, and returns a temp InvocationContext containing its state so it can
 621    * be restored later using {@link #restoreInvocationContext(org.jboss.cache.InvocationContext)}
 622    *
 623    * @param ctx the current context to be reset
 624    * @return a clone of ctx, before it was reset
 625    */
 626  4538 private InvocationContext resetInvocationContext(InvocationContext ctx)
 627    {
 628    // wipe current context.
 629  4538 cache.setInvocationContext(null);
 630  4538 return ctx;
 631    }
 632   
 633    /**
 634    * A map is deemed 'safe' to be passed as-is to a listener, if either of the following are true:
 635    * <ul>
 636    * <li>It is null</li>
 637    * <li>It is an instance of {@link org.jboss.cache.util.MapCopy}, which is immutable</li>
 638    * <li>It is an instance of {@link java.util.Collections#emptyMap()}, which is also immutable</li>
 639    * <li>It is an instance of {@link java.util.Collections#singletonMap(Object,Object)}, which is also immutable</li>
 640    * </ul>
 641    *
 642    * @param map
 643    * @return
 644    */
 645  889496 private boolean safe(Map map)
 646    {
 647  889496 return map == null || map instanceof MapCopy || map.getClass().equals(emptyMap) || map.getClass().equals(singletonMap);
 648    }
 649   
 650    /**
 651    * Class that encapsulates a valid invocation for a given registered listener - containing a reference to the
 652    * method to be invoked as well as the target object.
 653    */
 654    class ListenerInvocation
 655    {
 656    private Object target;
 657    private Method method;
 658   
 659  1300 public ListenerInvocation(Object target, Method method)
 660    {
 661  1300 this.target = target;
 662  1300 this.method = method;
 663    }
 664   
 665  4538 public void invoke(Event e)
 666    {
 667  4538 try
 668    {
 669  4538 method.invoke(target, e);
 670    }
 671    catch (InvocationTargetException e1)
 672    {
 673  5 Throwable cause = e1.getCause();
 674  5 if (cause != null)
 675  5 throw new CacheException("Caught exception invoking method " + method + " on listener instance " + target, cause);
 676    else
 677  0 throw new CacheException("Caught exception invoking method " + method + " on listener instance " + target, e1);
 678    }
 679    catch (IllegalAccessException e1)
 680    {
 681  0 log.warn("Unable to invoke method " + method + " on Object instance " + target + " - removing this target object from list of listeners!", e1);
 682  0 removeCacheListener(this.target);
 683    }
 684    }
 685    }
 686   
 687    }