FileDocCategorySizeDatePackage
NotificationListenerBase.javaAPI DocGlassfish v2 API10374Fri May 04 22:31:04 BST 2007com.sun.appserv.management.util.jmx

NotificationListenerBase.java

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 * 
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */
package com.sun.appserv.management.util.jmx;

import java.util.Set;
import java.util.HashSet;
import java.util.Collections;

import java.io.IOException;

import javax.management.ObjectName;
import javax.management.Notification;
import javax.management.NotificationListener;
import javax.management.NotificationFilter;
import javax.management.MBeanServerNotification;
import javax.management.MBeanServer;
import javax.management.MBeanServerConnection;
import javax.management.InstanceNotFoundException;
import javax.management.JMException;

import com.sun.appserv.management.util.misc.GSetUtil;

/**
	Convenience base class for listening for Notifications
	from one or more MBeans, which may be specified as
	a specific MBean ObjectName, or an ObjectName pattern.
	If the ObjectName is a pattern, the list of listenees
	is dynamically maintained.
	<p>
	Caller should call {@link #cleanup} when done, because
	a listener is maintained on the MBeanServer delegate.
	
 */
public abstract class NotificationListenerBase
    implements NotificationListener
{
    private final String                mName;
    private final MBeanServerConnection mConn;
    
    /** actual MBean ObjectNames, not patterns */
	private final Set<ObjectName>	mListenees;
	
	/** targets as specified by caller, may be a pattern or fixed ObjectName */
	private final ObjectName            mPattern;
	private final NotificationFilter    mFilter;
	private final Object                mHandback;
	
	private RegistrationListener  mDelegateListener;
    
    private volatile boolean            mSetupListening;
        
	/**
	    Calls this( conn, listenTo, null, null ).
        <p><b>Instantiating code must call setupListening() in order to initiate
        listening</b>
	 */
		protected
	NotificationListenerBase(
        final String                name,
	    final MBeanServerConnection conn,
	    final ObjectName            pattern )
		throws InstanceNotFoundException, IOException
	{
	    this( name, conn, pattern, null );
	}
	
	/**
	    Listen to all MBean(s) which match the pattern 'listenTo'.
        <p><b>Instantiating code must call setupListening() in order to initiate
        listening</b>
        @param listenerName arbitrary name of this listener
	    @param conn the MBeanServerConnection or MBeanServer
	    @param pattern an MBean ObjectName, or an ObjectName pattern
	    @param filter optional NotificationFilter
	 */
		protected
	NotificationListenerBase(
        final String                name,
	    final MBeanServerConnection conn,
	    final ObjectName            pattern,
	    final NotificationFilter    filter )
		throws InstanceNotFoundException, IOException
	{
        mName           = name;
	    mConn           = conn;
		mPattern        = pattern;
		mFilter         = filter;
	    mHandback       = null;
	    mDelegateListener   = null;
        mSetupListening = false;
	    
		mListenees		= Collections.synchronizedSet( new HashSet<ObjectName>() );
		
	    // test connection for validity
	    if ( ! conn.isRegistered( JMXUtil.getMBeanServerDelegateObjectName() ) )
	    {
	        throw new IllegalArgumentException();
	    }
	}

    /**
        Subclass should implement this routine.
     */
	public abstract void
	    handleNotification( final Notification notif, final Object handback);
	
	
		protected synchronized void
	listenToMBean( final ObjectName objectName )
		throws InstanceNotFoundException, IOException
	{
	    if ( ! mListenees.contains( objectName ) )
	    {
    	    mListenees.add( objectName );
    		getMBeanServerConnection().addNotificationListener(
    		    objectName, this, mFilter, null );
	    }
	}
	
		public synchronized void
	startListening()
		throws InstanceNotFoundException, IOException
	{
        if ( mSetupListening )
        {
            throw new IllegalStateException( "setupListening() must be called exactly once" );
        }
        
	    if ( mPattern.isPattern() )
	    {
    		// it's crucial we listen for registration/unregistration events
    		// so that any patterns are maintained.
    		// do this BEFORE the code below, of we could
    		// miss a registration.
	        mDelegateListener   = new RegistrationListener();
    		JMXUtil.listenToMBeanServerDelegate( mConn,
    		    mDelegateListener, null, null );
	    }
	    
	    
		Set<ObjectName>	s	= null;
		
		if ( mPattern.isPattern() )
		{
			s	= JMXUtil.queryNames( getConn(), mPattern, null );
		}
		else
		{
			s	= GSetUtil.newSet( mPattern );
		}
		
		synchronized( this )
		{
    		for( final ObjectName objectName : s )
    		{
    		    listenToMBean( objectName );
    		}
		}
        
        mSetupListening = true;
	}
	
	/**
	    Get the filter originally specified when constructing this object.
	 */
		public final NotificationFilter
	getNotificationFilter( final ObjectName objectName)
	{
	    return mFilter;
	}
	
	
		protected synchronized void
	listenToIfMatch( final ObjectName objectName )
	    throws IOException, InstanceNotFoundException
	{
		if ( ! mListenees.contains( objectName ) )
		{
			final String	defaultDomain	= getConn().getDefaultDomain();
			
			if ( JMXUtil.matchesPattern( defaultDomain, mPattern, objectName ) )
			{
				listenToMBean( objectName );
			}
		}
	}
	/**
	    tracks coming and going of MBeans being listened to which
	    match our patterns.
	  */
	private final class RegistrationListener implements NotificationListener
	{
		public RegistrationListener()	{}
		
			public void
		handleNotification(
			final Notification	notifIn, 
			final Object		handback) 
		{
			if ( notifIn instanceof MBeanServerNotification )
			{
				final MBeanServerNotification notif	= (MBeanServerNotification)notifIn;
				
				final ObjectName objectName	= notif.getMBeanName();
				final String     type	= notif.getType();
				
				try
				{
    				if ( type.equals( MBeanServerNotification.REGISTRATION_NOTIFICATION  ) )
    				{
    					listenToIfMatch( objectName );
    				}
    				else if ( type.equals( MBeanServerNotification.UNREGISTRATION_NOTIFICATION  ) )
    				{
    	                mListenees.remove( objectName );
    				}
				}
				catch( Exception e )
				{
				    // nothing can be done...
				}
			}
		}
	}
	
	/**
	    Reset everything so that no listening is occuring and
	    all lists are empty.
	 */
	    public synchronized void
	cleanup()
	{
	    try
	    {
    	    if ( mDelegateListener != null )
    	    {
        		// it's crucial we listen for registration/unregistration events
        		// so that any patterns are maintained.
        		getConn().removeNotificationListener(
        		    JMXUtil.getMBeanServerDelegateObjectName(),
        			mDelegateListener, null, null );
        	    mDelegateListener   = null;
    	    }
    	    
    		for( final ObjectName objectName : mListenees )
    		{
        	    getConn().removeNotificationListener(
        	        objectName, this, mFilter, null);
    		}
		}
		catch( JMException e )
		{
		}
		catch( IOException e )
		{
		}
		
		mListenees.clear();
	}
	
	/**
	    @return a copy of the MBean currently being listened to.
	 */
		public synchronized Set<ObjectName>
	getListenees()
	{
		final Set<ObjectName>	objectNames	= new HashSet<ObjectName>();
		
		synchronized( mListenees )
		{
			objectNames.addAll( mListenees );
		}
		
		return( objectNames );
	}
	
	
    /**
        @return the MBeanServerConnection in use.
        @throws an Exception if no longer alive ( isAlive() returns false).
     */
	    public final MBeanServerConnection
    getMBeanServerConnection()
    {
        return getConn();
    }
    
        protected final MBeanServerConnection
    getConn()
    {
        return mConn;
    }
    
    
	    protected final void
    checkAlive()
        throws IOException
    {
        if ( ! isAlive() )
        {
            throw new IOException( "MBeanServerConnection failed" );
        }
    }
    
    /**
        @return true if still listening and the connection is still alive
     */
        public boolean
    isAlive()
    {
        boolean isAlive = true;
        
        if ( ! (mConn instanceof MBeanServer) )
        {
            // remote, check if it is alive
            try
            {
                mConn.isRegistered( JMXUtil.getMBeanServerDelegateObjectName() );
            }
            catch( Exception e )
            {
                isAlive = false;
            }
        }
        return isAlive;
    }
	
}