3 Replies Latest reply on Jul 5, 2007 9:44 AM by Brian Stansberry

    @CacheListener impl must be public?

    Brian Stansberry Master

      Notifier.isValidListenerClass() enforces a requirement that the @CacheListener be declared public. Is that necessary for the inspection of annotations on the methods?

      In the AS codebase I have a number of inner class CacheListener impls that I'd modified to use the new API. But use of them fails at runtime due to the requirement that the class be public. I can of course fix this but it somewhat perturbs the object model. For sure other people are going to stumble onto the same problem, since an inner class subclassing AbstractCacheListener was likely a very common way to use the old API.

      If this requirement needs to be enforced, isValidListenerClass() should be modified to separately check it and throw an exception with a very explicit message explaining what the problem is. Took me quite a while and a careful reading of Notifier to figure it out.

        • 1. Re: @CacheListener impl must be public?
          Brian Stansberry Master

          I see now the exception message did mention the need to be publicly accessible. But the message was too verbose and the relevant bit wasn't in my log viewer window when I was trying to figure out why this failed.

          I separated the public accessibility check into a separate if statement; if it fails the exception message will only discuss that problem.

          • 2. Re: @CacheListener impl must be public?
            Manik Surtani Master

            It needs to be public because the callback is done via reflection.

            Good point re: the exception message, will do this for CR4/GA

            • 3. Re: @CacheListener impl must be public?
              Brian Stansberry Master

              I already changed the exception message bit. Do you want me to post a JIRA for that?

              I wrote a little test program and it had no trouble doing inspection on a private class and then invoking a public method on it:

              package org.jboss.cache.notifications;
              
              import org.jboss.cache.notifications.annotation.CacheListener;
              import org.jboss.cache.notifications.annotation.NodeModified;
              import org.jboss.cache.notifications.event.NodeModifiedEvent;
              
              public class Testee
              {
               public Object getCacheListener()
               {
               return new InnerClass();
               }
              
               @CacheListener
               private class InnerClass
               {
               @NodeModified
               public void nodeModified(NodeModifiedEvent event)
               {
               System.out.println("Node modified " + event);
               }
              
               public long getLong()
               {
               return 0L;
               }
               }
              }
              


              package org.jboss.cache.notifications;
              
              import java.lang.reflect.Method;
              
              import org.jboss.cache.notifications.annotation.CacheListener;
              import org.jboss.cache.notifications.annotation.NodeModified;
              import org.jboss.cache.notifications.event.EventImpl;
              
              public class Tester
              {
              
               public static void main(String[] args)
               {
               Testee testee = new Testee();
               Object listener = testee.getCacheListener();
              
               if (!listener.getClass().isAnnotationPresent(CacheListener.class))
               throw new IncorrectCacheListenerException("An Object must have the org.jboss.cache.notifications.annotation.CacheListener annotation to be considered a cache listener, and must be publicly accessible!");
               // now try all methods on the listener for anything that we like. Note that only PUBLIC methods are scanned.
               for (Method m : listener.getClass().getMethods())
               {
               if (m.isAnnotationPresent(NodeModified.class))
               {
               System.out.println("Annotation found");
               try
               {
               m.invoke(listener, new Object[] { new EventImpl() });
               }
               catch (Exception e)
               {
               e.printStackTrace(System.out);
               }
               System.out.println("Method invoked");
               return;
               }
               }
               System.out.println("Annotation not found");
               }
              }