/*
 * © Copyright 2004 Hewlett-Packard
 */
package test.mx4j.server;

import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.Iterator;

import javax.management.NotificationListener;
import javax.management.Notification;
import javax.management.ObjectName;
import javax.management.MBeanInfo;
import javax.management.ObjectInstance;

import test.MX4JTestCase;
import mx4j.server.interceptor.NotificationListenerMBeanServerInterceptor;
import mx4j.server.interceptor.MBeanServerInterceptor;
import mx4j.server.MBeanMetaData;
import mx4j.server.MBeanInvoker;

/**
 * $Rev$
 */
public class NotificationListenerMBeanServerInterceptorTest extends MX4JTestCase
{
   public NotificationListenerMBeanServerInterceptorTest(String name)
   {
      super(name);
   }

   /**
    * One listener, one MBean, one ObjectName
    */
   public void testAddRemove1() throws Exception
   {
      NotificationListenerMBeanServerInterceptor interceptor = new NotificationListenerMBeanServerInterceptor();
      interceptor.setEnabled(true);
      List chain = new ArrayList();
      chain.add(interceptor);
      chain.add(new NoOpMBeanServerInterceptor());
      interceptor.setChain(chain);

      ObjectName objectName = ObjectName.getInstance(":type=test");
      MBeanMetaData metadata = new NotificationListenerMBeanMetaData(this, objectName);
      NotificationListener listener = new NotificationListener()
      {
         public void handleNotification(Notification notification, Object handback)
         {
         }
      };

      interceptor.addNotificationListener(metadata, listener, null, null);
      assertEquals(1, interceptor.getNotificationListenerWrappers().size());
      Object wrapper = ((Map.Entry)interceptor.getNotificationListenerWrappers().entrySet().iterator().next()).getValue();
      assertEquals(1, interceptor.getNotificationListenerWrapperReferenceCount(wrapper));
      assertEquals(1, interceptor.getObjectNames().size());
      assertEquals(objectName, interceptor.getObjectNames().keySet().iterator().next());

      interceptor.removeNotificationListener(metadata, listener, null, null);
      assertEquals(0, interceptor.getNotificationListenerWrapperReferenceCount(wrapper));
      assertEquals(0, interceptor.getNotificationListenerWrappers().size());
      assertEquals(0, interceptor.getObjectNames().size());
   }

   /**
    * One listener thrice, one MBean, one ObjectName
    */
   public void testAddRemove2() throws Exception
   {
      NotificationListenerMBeanServerInterceptor interceptor = new NotificationListenerMBeanServerInterceptor();
      interceptor.setEnabled(true);
      List chain = new ArrayList();
      chain.add(interceptor);
      chain.add(new NoOpMBeanServerInterceptor());
      interceptor.setChain(chain);

      ObjectName objectName = ObjectName.getInstance(":type=test");
      MBeanMetaData metadata = new NotificationListenerMBeanMetaData(this, objectName);
      NotificationListener listener = new NotificationListener()
      {
         public void handleNotification(Notification notification, Object handback)
         {
         }
      };

      interceptor.addNotificationListener(metadata, listener, null, null);
      interceptor.addNotificationListener(metadata, listener, null, listener);
      Object handback = new Object();
      interceptor.addNotificationListener(metadata, listener, null, handback);

      assertEquals(1, interceptor.getNotificationListenerWrappers().size());
      Object wrapper = ((Map.Entry)interceptor.getNotificationListenerWrappers().entrySet().iterator().next()).getValue();
      assertEquals(3, interceptor.getNotificationListenerWrapperReferenceCount(wrapper));
      assertEquals(1, interceptor.getObjectNames().size());
      assertEquals(objectName, interceptor.getObjectNames().keySet().iterator().next());

      interceptor.removeNotificationListener(metadata, listener, null, handback);
      assertEquals(2, interceptor.getNotificationListenerWrapperReferenceCount(wrapper));
      assertEquals(1, interceptor.getObjectNames().size());

      interceptor.removeNotificationListener(metadata, listener);
      assertEquals(0, interceptor.getNotificationListenerWrapperReferenceCount(wrapper));
      assertEquals(0, interceptor.getNotificationListenerWrappers().size());
      assertEquals(0, interceptor.getObjectNames().size());
   }

   /**
    * One listener, one MBean, two ObjectNames
    */
   public void testAddRemove3() throws Exception
   {
      NotificationListenerMBeanServerInterceptor interceptor = new NotificationListenerMBeanServerInterceptor();
      interceptor.setEnabled(true);
      List chain = new ArrayList();
      chain.add(interceptor);
      chain.add(new NoOpMBeanServerInterceptor());
      interceptor.setChain(chain);

      MBeanMetaData metadata1 = new NotificationListenerMBeanMetaData(this, ObjectName.getInstance(":type=test1"));
      MBeanMetaData metadata2 = new NotificationListenerMBeanMetaData(this, ObjectName.getInstance(":type=test2"));
      NotificationListener listener = new NotificationListener()
      {
         public void handleNotification(Notification notification, Object handback)
         {
         }
      };

      interceptor.addNotificationListener(metadata1, listener, null, null);
      interceptor.addNotificationListener(metadata2, listener, null, null);

      assertEquals(2, interceptor.getNotificationListenerWrappers().size());
      assertEquals(2, interceptor.getObjectNames().size());

      interceptor.removeNotificationListener(metadata1, listener, null, null);
      assertEquals(1, interceptor.getNotificationListenerWrappers().size());
      assertEquals(1, interceptor.getObjectNames().size());

      interceptor.removeNotificationListener(metadata2, listener, null, null);
      assertEquals(0, interceptor.getNotificationListenerWrappers().size());
      assertEquals(0, interceptor.getObjectNames().size());
   }

   /**
    * One listener, two MBeans, two ObjectNames
    */
   public void testAddRemove4() throws Exception
   {
      NotificationListenerMBeanServerInterceptor interceptor = new NotificationListenerMBeanServerInterceptor();
      interceptor.setEnabled(true);
      List chain = new ArrayList();
      chain.add(interceptor);
      chain.add(new NoOpMBeanServerInterceptor());
      interceptor.setChain(chain);

      MBeanMetaData metadata1 = new NotificationListenerMBeanMetaData(this, ObjectName.getInstance(":type=test1"));
      MBeanMetaData metadata2 = new NotificationListenerMBeanMetaData(interceptor, ObjectName.getInstance(":type=test2"));
      NotificationListener listener = new NotificationListener()
      {
         public void handleNotification(Notification notification, Object handback)
         {
         }
      };

      interceptor.addNotificationListener(metadata1, listener, null, null);
      interceptor.addNotificationListener(metadata2, listener, null, null);

      assertEquals(2, interceptor.getNotificationListenerWrappers().size());
      assertEquals(2, interceptor.getObjectNames().size());

      interceptor.removeNotificationListener(metadata1, listener, null, null);
      assertEquals(1, interceptor.getNotificationListenerWrappers().size());
      assertEquals(1, interceptor.getObjectNames().size());

      interceptor.removeNotificationListener(metadata2, listener, null, null);
      assertEquals(0, interceptor.getNotificationListenerWrappers().size());
      assertEquals(0, interceptor.getObjectNames().size());
   }

   /**
    * Two listeners, one MBean, one ObjectName
    */
   public void testAddRemove5() throws Exception
   {
      NotificationListenerMBeanServerInterceptor interceptor = new NotificationListenerMBeanServerInterceptor();
      interceptor.setEnabled(true);
      List chain = new ArrayList();
      chain.add(interceptor);
      chain.add(new NoOpMBeanServerInterceptor());
      interceptor.setChain(chain);

      ObjectName objectName = ObjectName.getInstance(":type=test1");
      MBeanMetaData metadata = new NotificationListenerMBeanMetaData(this, objectName);
      NotificationListener listener1 = new NotificationListener()
      {
         public void handleNotification(Notification notification, Object handback)
         {
         }
      };
      NotificationListener listener2 = new NotificationListener()
      {
         public void handleNotification(Notification notification, Object handback)
         {
         }
      };

      interceptor.addNotificationListener(metadata, listener1, null, null);
      interceptor.addNotificationListener(metadata, listener2, null, null);

      assertEquals(2, interceptor.getNotificationListenerWrappers().size());
      for (Iterator iterator = interceptor.getNotificationListenerWrappers().values().iterator(); iterator.hasNext();)
      {
         Object wrapper = iterator.next();
         assertEquals(1, interceptor.getNotificationListenerWrapperReferenceCount(wrapper));
      }
      assertEquals(1, interceptor.getObjectNames().size());
      assertEquals(objectName, interceptor.getObjectNames().keySet().iterator().next());

      interceptor.removeNotificationListener(metadata, listener1);
      assertEquals(1, interceptor.getNotificationListenerWrappers().size());
      assertEquals(1, interceptor.getObjectNames().size());

      interceptor.removeNotificationListener(metadata, listener2);
      assertEquals(0, interceptor.getNotificationListenerWrappers().size());
      assertEquals(0, interceptor.getObjectNames().size());
   }

   /**
    * Two listeners, one MBean, two ObjectNames
    */
   public void testAddRemove6() throws Exception
   {
      NotificationListenerMBeanServerInterceptor interceptor = new NotificationListenerMBeanServerInterceptor();
      interceptor.setEnabled(true);
      List chain = new ArrayList();
      chain.add(interceptor);
      chain.add(new NoOpMBeanServerInterceptor());
      interceptor.setChain(chain);

      MBeanMetaData metadata1 = new NotificationListenerMBeanMetaData(this, ObjectName.getInstance(":type=test1"));
      MBeanMetaData metadata2 = new NotificationListenerMBeanMetaData(this, ObjectName.getInstance(":type=test2"));
      NotificationListener listener1 = new NotificationListener()
      {
         public void handleNotification(Notification notification, Object handback)
         {
         }
      };
      NotificationListener listener2 = new NotificationListener()
      {
         public void handleNotification(Notification notification, Object handback)
         {
         }
      };

      interceptor.addNotificationListener(metadata1, listener1, null, null);
      interceptor.addNotificationListener(metadata2, listener2, null, null);

      assertEquals(2, interceptor.getNotificationListenerWrappers().size());
      for (Iterator iterator = interceptor.getNotificationListenerWrappers().values().iterator(); iterator.hasNext();)
      {
         Object wrapper = iterator.next();
         assertEquals(1, interceptor.getNotificationListenerWrapperReferenceCount(wrapper));
      }
      assertEquals(2, interceptor.getObjectNames().size());

      interceptor.removeNotificationListener(metadata1, listener1);
      assertEquals(1, interceptor.getNotificationListenerWrappers().size());
      assertEquals(1, interceptor.getObjectNames().size());

      interceptor.removeNotificationListener(metadata2, listener2);
      assertEquals(0, interceptor.getNotificationListenerWrappers().size());
      assertEquals(0, interceptor.getObjectNames().size());
   }

   /**
    * One listener, one MBean, one ObjectName
    */
   public void testAddUnregister1() throws Exception
   {
      NotificationListenerMBeanServerInterceptor interceptor = new NotificationListenerMBeanServerInterceptor();
      interceptor.setEnabled(true);
      List chain = new ArrayList();
      chain.add(interceptor);
      chain.add(new NoOpMBeanServerInterceptor());
      interceptor.setChain(chain);

      MBeanMetaData metadata = new NotificationListenerMBeanMetaData(this, ObjectName.getInstance(":type=test1"));
      NotificationListener listener = new NotificationListener()
      {
         public void handleNotification(Notification notification, Object handback)
         {
         }
      };

      interceptor.addNotificationListener(metadata, listener, null, null);
      interceptor.registration(metadata, MBeanServerInterceptor.POST_DEREGISTER);
      assertEquals(0, interceptor.getNotificationListenerWrappers().size());
      assertEquals(0, interceptor.getObjectNames().size());
   }

   /**
    * One listener twice, one MBean, one ObjectName
    */
   public void testAddUnregister2() throws Exception
   {
      NotificationListenerMBeanServerInterceptor interceptor = new NotificationListenerMBeanServerInterceptor();
      interceptor.setEnabled(true);
      List chain = new ArrayList();
      chain.add(interceptor);
      chain.add(new NoOpMBeanServerInterceptor());
      interceptor.setChain(chain);

      MBeanMetaData metadata = new NotificationListenerMBeanMetaData(this, ObjectName.getInstance(":type=test1"));
      NotificationListener listener = new NotificationListener()
      {
         public void handleNotification(Notification notification, Object handback)
         {
         }
      };

      interceptor.addNotificationListener(metadata, listener, null, null);
      interceptor.addNotificationListener(metadata, listener, null, listener);
      interceptor.registration(metadata, MBeanServerInterceptor.POST_DEREGISTER);
      assertEquals(0, interceptor.getNotificationListenerWrappers().size());
      assertEquals(0, interceptor.getObjectNames().size());
   }

   /**
    * One listener, one MBean, two ObjectNames
    */
   public void testAddUnregister3() throws Exception
   {
      NotificationListenerMBeanServerInterceptor interceptor = new NotificationListenerMBeanServerInterceptor();
      interceptor.setEnabled(true);
      List chain = new ArrayList();
      chain.add(interceptor);
      chain.add(new NoOpMBeanServerInterceptor());
      interceptor.setChain(chain);

      MBeanMetaData metadata1 = new NotificationListenerMBeanMetaData(this, ObjectName.getInstance(":type=test1"));
      MBeanMetaData metadata2 = new NotificationListenerMBeanMetaData(this, ObjectName.getInstance(":type=test2"));
      NotificationListener listener = new NotificationListener()
      {
         public void handleNotification(Notification notification, Object handback)
         {
         }
      };

      interceptor.addNotificationListener(metadata1, listener, null, null);
      interceptor.addNotificationListener(metadata2, listener, null, null);
      interceptor.registration(metadata1, MBeanServerInterceptor.POST_DEREGISTER);
      assertEquals(1, interceptor.getNotificationListenerWrappers().size());
      assertEquals(1, interceptor.getObjectNames().size());

      interceptor.registration(metadata2, MBeanServerInterceptor.POST_DEREGISTER);
      assertEquals(0, interceptor.getNotificationListenerWrappers().size());
      assertEquals(0, interceptor.getObjectNames().size());
   }

   /**
    * Two listeners, one MBean, two ObjectNames
    */
   public void testAddUnregister4() throws Exception
   {
      NotificationListenerMBeanServerInterceptor interceptor = new NotificationListenerMBeanServerInterceptor();
      interceptor.setEnabled(true);
      List chain = new ArrayList();
      chain.add(interceptor);
      chain.add(new NoOpMBeanServerInterceptor());
      interceptor.setChain(chain);

      MBeanMetaData metadata1 = new NotificationListenerMBeanMetaData(this, ObjectName.getInstance(":type=test1"));
      MBeanMetaData metadata2 = new NotificationListenerMBeanMetaData(this, ObjectName.getInstance(":type=test2"));
      NotificationListener listener1 = new NotificationListener()
      {
         public void handleNotification(Notification notification, Object handback)
         {
         }
      };
      NotificationListener listener2 = new NotificationListener()
      {
         public void handleNotification(Notification notification, Object handback)
         {
         }
      };

      interceptor.addNotificationListener(metadata1, listener1, null, null);
      interceptor.addNotificationListener(metadata2, listener2, null, null);
      interceptor.registration(metadata1, MBeanServerInterceptor.POST_DEREGISTER);
      assertEquals(1, interceptor.getNotificationListenerWrappers().size());
      assertEquals(1, interceptor.getObjectNames().size());

      interceptor.registration(metadata2, MBeanServerInterceptor.POST_DEREGISTER);
      assertEquals(0, interceptor.getNotificationListenerWrappers().size());
      assertEquals(0, interceptor.getObjectNames().size());
   }

   private class NotificationListenerMBeanMetaData implements MBeanMetaData
   {
      private final Object mbean;
      private final ObjectName objectName;

      public NotificationListenerMBeanMetaData(Object mbean, ObjectName objectName)
      {
         this.mbean = mbean;
         this.objectName = objectName;
      }

      public Object getMBean()
      {
         return mbean;
      }

      public ObjectName getObjectName()
      {
         return objectName;
      }

      public void setMBean(Object mbean)
      {
      }

      public void setClassLoader(ClassLoader loader)
      {
      }

      public ClassLoader getClassLoader()
      {
         return null;
      }

      public void setObjectName(ObjectName name)
      {
      }

      public void setMBeanInfo(MBeanInfo info)
      {
      }

      public MBeanInfo getMBeanInfo()
      {
         return null;
      }

      public void setMBeanInterface(Class management)
      {
      }

      public Class getMBeanInterface()
      {
         return null;
      }

      public void setMBeanStandard(boolean value)
      {
      }

      public boolean isMBeanStandard()
      {
         return false;
      }

      public void setMBeanDynamic(boolean value)
      {
      }

      public boolean isMBeanDynamic()
      {
         return false;
      }

      public void setMBeanInvoker(MBeanInvoker invoker)
      {
      }

      public MBeanInvoker getMBeanInvoker()
      {
         return null;
      }

      public ObjectInstance getObjectInstance()
      {
         return null;
      }
   }
}
