# MBeanTracker.java

    /*
     * Licensed to the Apache Software Foundation (ASF) under one
     * or more contributor license agreements.  See the NOTICE file
     * distributed with this work for additional information
     * regarding copyright ownership.  The ASF licenses this file
     * to you under the Apache License, Version 2.0 (the
     * "License"); you may not use this file except in compliance
     * with the License.  You may obtain a copy of the License at
     *
     *   http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing,
     * software distributed under the License is distributed on an
     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     * KIND, either express or implied.  See the License for the
     * specific language governing permissions and limitations
     * under the License.
     */
    package org.qi4j.library.jmx;

    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import javax.management.*;
    import org.slf4j.LoggerFactory;

    /**
     * TODO
     */
    public class MBeanTracker
    {
        private boolean started = false;
        private ObjectName pattern;
        private MBeanServer server;
        private List<TrackerCallback> callbacks = new ArrayList<TrackerCallback>();
        private List<ObjectName> tracked = Collections.synchronizedList( new ArrayList<ObjectName>() );
        private RegistrationListener registrationListener;

        public MBeanTracker(ObjectName pattern, MBeanServer server)
        {
           this.pattern = pattern;
           this.server = server;
        }

        public MBeanTracker registerCallback(TrackerCallback callback)
        {
           List<TrackerCallback> newList = new ArrayList<TrackerCallback>(callbacks);
           newList.add(callback);
           callbacks = newList;
           return this;
        }

        public MBeanTracker unregisterCallback(TrackerCallback callback)
        {
           List<TrackerCallback> newList = new ArrayList<TrackerCallback>(callbacks);
           newList.remove(callback);
           callbacks = newList;
           return this;
        }

        public Iterable<ObjectName> getTracked()
        {
           return tracked;
        }

        public void start()
        {
           if (!started)
           {
              try
              {
                 registrationListener = new RegistrationListener();
                 server.addNotificationListener(new ObjectName("JMImplementation:type=MBeanServerDelegate"), registrationListener, null, null);

                 for (ObjectName objectName : server.queryNames(pattern, null))
                 {
                    for (TrackerCallback callback : callbacks)
                    {
                       try
                       {
                          tracked.add(objectName);
                          callback.addedMBean(objectName, server);
                       } catch (Throwable throwable)
                       {
                          LoggerFactory.getLogger( MBeanTracker.class ).error("Tracker callback threw exception", throwable);
                       }
                    }
                 }

                 started = true;
              } catch (Exception e)
              {
                 // Could not start
              }
           }
        }

        public void stop()
        {
           if (started)
           {
              try
              {
                 server.removeNotificationListener(new ObjectName("JMImplementation:type=MBeanServerDelegate"), registrationListener);

                 for (ObjectName objectName : server.queryNames(pattern, null))
                 {
                    for (TrackerCallback callback : callbacks)
                    {
                       try
                       {
                          callback.removedMBean(objectName, server);
                          tracked.remove(objectName);
                       } catch (Throwable throwable)
                       {
                          LoggerFactory.getLogger(MBeanTracker.class).error("Tracker callback threw exception", throwable);
                       }
                    }
                 }

                 started = false;
              } catch (Exception e)
              {
                 // Could not start
              }
           }
        }

        /**
         * Callback that is used to notify when MBeans matching the given pattern becomes
         * added or removed.
         */
        public interface TrackerCallback
        {
           void addedMBean(ObjectName newMBean, MBeanServer server)
              throws Throwable;

           void removedMBean(ObjectName removedMBean, MBeanServer server)
              throws Throwable;
        }

        /**
         * Registers the given NotificationListener+NotificationFilter on tracked MBeans.
         */
        public static class NotificationTracker
            implements TrackerCallback
        {
            private NotificationListener listener;
            private NotificationFilter filter;

            public NotificationTracker( NotificationListener listener, NotificationFilter filter )
            {
                this.listener = listener;
                this.filter = filter;
            }

            @Override
            public void addedMBean( ObjectName newMBean, MBeanServer server ) throws Throwable
            {
                server.addNotificationListener( newMBean, listener, filter,  null);
            }

            @Override
            public void removedMBean( ObjectName removedMBean, MBeanServer server ) throws Throwable
            {
                server.removeNotificationListener( removedMBean, listener );
            }
        }

        private class RegistrationListener
           implements NotificationListener
        {
           @Override
           public void handleNotification(Notification notification, Object o)
           {
              if (notification instanceof MBeanServerNotification )
              {
                 MBeanServerNotification serverNotification = (MBeanServerNotification) notification;
                 if (pattern.apply(serverNotification.getMBeanName()))
                 {
                    if (serverNotification.getType().equals(MBeanServerNotification.REGISTRATION_NOTIFICATION))
                    {
                       for (TrackerCallback callback : callbacks)
                       {
                          try
                          {
                             tracked.add(serverNotification.getMBeanName());
                             callback.addedMBean(serverNotification.getMBeanName(), server);
                          } catch (Throwable throwable)
                          {
                             LoggerFactory.getLogger(MBeanTracker.class).error("Tracker callback threw exception", throwable);
                          }
                       }
                    } else if (serverNotification.getType().equals(MBeanServerNotification.UNREGISTRATION_NOTIFICATION))
                    {
                       for (TrackerCallback callback : callbacks)
                       {
                          try
                          {
                             callback.removedMBean(serverNotification.getMBeanName(), server);
                             tracked.remove(serverNotification.getMBeanName());
                          } catch (Throwable throwable)
                          {
                             LoggerFactory.getLogger(MBeanTracker.class).error("Tracker callback threw exception", throwable);
                          }
                       }
                    }
                 }
              }
           }
        }
    }