/*
* 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.client.handler;
import java.io.IOException;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Collections;
import java.util.logging.Logger;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.MBeanInfo;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanServerConnection;
import javax.management.NotificationListener;
import javax.management.NotificationFilter;
import javax.management.ListenerNotFoundException;
import javax.management.ObjectName;
import javax.management.NotificationBroadcaster;
import javax.management.ReflectionException;
import javax.management.IntrospectionException;
import javax.management.JMException;
import javax.management.InstanceNotFoundException;
import javax.management.AttributeNotFoundException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanException;
import java.lang.reflect.Proxy;
import java.lang.reflect.Method;
import com.sun.appserv.management.base.AMX;
import com.sun.appserv.management.base.AMXDebug;
import com.sun.appserv.management.base.AMXAttributes;
import com.sun.appserv.management.base.Extra;
import com.sun.appserv.management.base.AMXClientLogger;
import com.sun.appserv.management.base.Util;
import com.sun.appserv.management.client.ConnectionSource;
import com.sun.appserv.management.util.jmx.MBeanProxyHandler;
import com.sun.appserv.management.util.jmx.JMXUtil;
import com.sun.appserv.management.util.misc.ClassUtil;
import com.sun.appserv.management.util.misc.GSetUtil;
import com.sun.appserv.management.util.misc.TypeCast;
import com.sun.appserv.management.util.misc.StringUtil;
import com.sun.appserv.management.util.stringifier.SmartStringifier;
import com.sun.appserv.management.util.stringifier.ArrayStringifier;
import com.sun.appserv.management.DomainRoot;
import com.sun.appserv.management.base.Container;
import com.sun.appserv.management.client.ProxyFactory;
/**
Extends MBeanProxyHandler by also supporting the functionality required of an AMX.
<p><b>THREAD SAFE</b>
*/
class AMXProxyHandler extends MBeanProxyHandler
implements Extra
{
protected final PerMBeanCache mCache;
private boolean mCheckedForInvariantMBeanInfo = false;
static protected final String DEBUG_ID =
"com.sun.appserv.management.client.handler.AMXProxyHandler";
protected String
getDebugID()
{
return DEBUG_ID;
}
/**
Create a new AMX proxy.
@param connectionSource the connection
@param proxiedMBeanObjectName the ObjectName of the proxied MBean
*/
protected
AMXProxyHandler(
final ConnectionSource connectionSource,
final ObjectName proxiedMBeanObjectName )
throws IOException
{
super( connectionSource, proxiedMBeanObjectName );
mDebug = AMXDebug.getInstance().getOutput( getDebugID() );
mCache = new PerMBeanCache();
setProxyLogger( AMXClientLogger.getInstance() );
}
protected void
cacheAttribute( final Attribute attr )
{
mCache.cacheAttribute( attr );
}
/**
Get an Attribute, first from the cache, but if not in the cache, fetching a new
copy, then caching it. This routine should only be used on invariant Attributes.
*/
protected Attribute
getCachedAttribute( final String attrName )
throws IOException, InstanceNotFoundException,
MBeanException, AttributeNotFoundException, ReflectionException
{
Attribute attr = mCache.getCachedAttribute( attrName );
if ( attr == null )
{
final MBeanServerConnection conn = getConnection();
final Object value = getConnection().getAttribute( getTargetObjectName(), attrName );
attr = new Attribute( attrName, value );
mCache.cacheAttribute( attr );
}
return( attr );
}
protected Object
getCachedAttributeValue( final String attrName )
throws IOException, JMException
{
final Attribute attr = getCachedAttribute( attrName );
assert( attr != null ) : "getCachedAttributeValue: null for " + attrName;
return( attr == null ? null : attr.getValue() );
}
/**
All proxies cached by ObjectName get cached by the ProxyFactory.
Proxies keyed by other values may not be unique and need to be cached
as items in mCache.
*/
private AMX
getCachedProxy( Object key )
{
AMX proxy = null;
if ( key instanceof ObjectName )
{
proxy = getProxyFactory().getProxy( (ObjectName)key, AMX.class );
}
else
{
proxy = Util.asAMX(mCache.getCachedItem( key ) );
}
if ( proxy != null )
{
final AMXProxyHandler handler = (AMXProxyHandler)Proxy.getInvocationHandler( proxy );
if ( ! handler.targetIsValid() )
{
debug( "removing cached proxy for key: ", key );
mCache.remove( key );
proxy = null;
}
}
return( proxy );
}
/**
A proxy cached by ObjectName can safely be shared globally, since the
ObjectNames are unique per connection. But non-ObjectName keys may
actually conflict from like MBeans
*/
private void
cacheProxy( final String key, final AMX proxy)
{
mCache.cacheItem( key, proxy );
}
private static final String CREATE = "create";
private static final String GET = "get";
private static final String MAP_SUFFIX = "Map";
private static final String SET_SUFFIX = "Set";
private static final String LIST_SUFFIX = "List";
private static final String OBJECT_NAME_MAP_SUFFIX= "ObjectName" + MAP_SUFFIX;
private static final String OBJECT_NAME_SET_SUFFIX= "ObjectName" + SET_SUFFIX;
private static final String OBJECT_NAME_LIST_SUFFIX= "ObjectName" + LIST_SUFFIX;
private static final String OBJECT_NAME_SUFFIX = "ObjectName";
private static final String CONTAINEE_J2EE_TYPES = Container.ATTR_CONTAINEE_J2EE_TYPES;
private static final String CONTAINER = "Container";
private static final String DOMAIN_ROOT = "DomainRoot";
private static final String MBEAN_INFO = "MBeanInfo";
private static final String ATTRIBUTE_NAMES = "AttributeNames";
private static final String J2EE_NAME = "Name";
private static final String J2EE_TYPE = "J2EEType";
public final static String ADD_NOTIFICATION_LISTENER = "addNotificationListener";
public final static String REMOVE_NOTIFICATION_LISTENER = "removeNotificationListener";
private final static String QUERY = "query";
protected ObjectName
getContainerObjectName()
throws IOException, JMException
{
return( (ObjectName)getCachedAttributeValue( AMXAttributes.ATTR_CONTAINER_OBJECT_NAME ) );
}
protected Class
getProxyInterface( final ObjectName objectName )
throws IOException, JMException, ClassNotFoundException
{
// by fetching a proxy this way, it may already exist, with an already-cached
// interface.
final AMX proxy = getProxyFactory().getProxy( objectName, AMX.class );
final Class proxyInterface = ClassUtil.getClassFromName( Util.getExtra( proxy ).getInterfaceName() );
return( proxyInterface );
}
private String
_getInterfaceName()
throws IOException, JMException
{
return( (String)getCachedAttributeValue( AMXAttributes.ATTR_INTERFACE_NAME ) );
}
public String
getInterfaceName()
{
try
{
return( _getInterfaceName() );
}
catch( Exception e )
{
throw new RuntimeException( e );
}
}
public ProxyFactory
getProxyFactory()
{
return( ProxyFactory.getInstance( getConnectionSource() ) );
}
/**
Get the proxy which is the parent of this one.
*/
synchronized Container
getContainer( final AMX myProxy )
throws IOException, JMException,
ClassNotFoundException
{
Container containerProxy = null;
if ( ! ( myProxy instanceof DomainRoot ) )
{
final ObjectName objectName = getContainerObjectName();
// a few MBeans propogated from other instances, such as Logging,
// do not have a Container.
if ( objectName != null )
{
containerProxy =
getProxyFactory().getProxy( objectName, Container.class );
}
}
return( containerProxy );
}
/**
Get the proxy corresponding to the DomainMBean for the domain to which
this proxy corresponds.
*/
private final DomainRoot
getDomainRoot( )
throws IOException
{
return( getProxyFactory().getDomainRoot( ) );
}
private static final String STRING = String.class.getName();
private static final String[] EMPTY_SIG = new String[ 0 ];
private static final String[] STRING_SIG = new String[] { STRING } ;
private static final String[] STRING2_SIG = new String[] { STRING, STRING } ;
private static final String GET_SINGLETON_CONTAINEE = "getSingletonContainee";
private static final String GET_CONTAINEE = "getContainee";
private static final String GET_SINGLETON_CONTAINEE_OBJECT_NAME=
GET_SINGLETON_CONTAINEE + OBJECT_NAME_SUFFIX;
private static final String[] GET_SINGLETON_CONTAINEE_OBJECT_NAME_SIG1 = STRING_SIG;
private static final String[] GET_SINGLETON_CONTAINEE_OBJECT_NAME_SIG2 = STRING2_SIG;
private static final String[] GET_OBJECT_NAMES_SIG_EMPTY = EMPTY_SIG;
private static final String[] GET_OBJECT_NAMES_SIG_STRING = STRING_SIG;
protected synchronized AMX
createProxy(
final ObjectName objectName )
{
return( getProxyFactory().getProxy( objectName, AMX.class ) );
}
/**
Return true if the method is one that is requesting a Map of AMX object.
*/
protected static boolean
isProxyMapGetter(
final Method method,
final int argCount )
{
boolean isProxyMapGetter = false;
final String name = method.getName();
if ( name.startsWith( GET ) &&
name.endsWith( MAP_SUFFIX ) &&
(! name.endsWith( OBJECT_NAME_MAP_SUFFIX )) &&
argCount <= 1 &&
Map.class.isAssignableFrom( method.getReturnType() ) )
{
isProxyMapGetter = true;
}
return( isProxyMapGetter );
}
/**
Return true if the method is one that is requesting a List of AMX object.
*/
protected static boolean
isProxyListGetter(
final Method method,
final int argCount )
{
boolean isProxyListGetter = false;
final String name = method.getName();
if ( ( name.startsWith( GET ) || name.startsWith( QUERY ) ) &&
name.endsWith( LIST_SUFFIX ) &&
(! name.endsWith( OBJECT_NAME_LIST_SUFFIX )) &&
argCount <= 1 &&
List.class.isAssignableFrom( method.getReturnType() ) )
{
isProxyListGetter = true;
}
return( isProxyListGetter );
}
/**
Return true if the method is one that is requesting a single AMX object.
Such methods are client-side methods and do not operate on the target MBean.
*/
protected static boolean
isSingleProxyGetter( final Method method, final int argCount )
{
boolean isProxyGetter = false;
final String name = method.getName();
if ( ( name.startsWith( GET ) || name.startsWith( QUERY ) ) &&
argCount <= 2 &&
AMX.class.isAssignableFrom( method.getReturnType() ) )
{
isProxyGetter = true;
}
return( isProxyGetter );
}
/**
@return true if the method is one that is requesting a Set of AMX.
*/
protected static boolean
isProxySetGetter( final Method method, final int argCount )
{
boolean isProxySetGetter = false;
final String name = method.getName();
if ( ( name.startsWith( GET ) || name.startsWith( QUERY ) ) &&
name.endsWith( SET_SUFFIX ) &&
!name.endsWith( OBJECT_NAME_SET_SUFFIX ) &&
argCount <= 2 &&
Set.class.isAssignableFrom( method.getReturnType() ) )
{
isProxySetGetter = true;
}
return( isProxySetGetter );
}
private static String
proxyGetterToObjectNameGetter( final String methodName )
{
return( methodName + OBJECT_NAME_SUFFIX );
}
private Object
invokeTarget(
final String methodName,
final Object[] args,
final String[] sig )
throws IOException, ReflectionException, InstanceNotFoundException, MBeanException,
AttributeNotFoundException
{
final int numArgs = args == null ? 0 : args.length;
Object result = null;
if ( numArgs == 0 &&
methodName.startsWith( GET ) )
{
final String attributeName = StringUtil.stripPrefix( methodName, GET );
result = getConnection().getAttribute( getTargetObjectName(), attributeName );
}
else
{
result = getConnection().invoke( getTargetObjectName(), methodName, args, sig );
}
return result;
}
private String
getJ2EEType( final Class c )
{
return( (String)ClassUtil.getFieldValue( c, "J2EE_TYPE" ) );
}
/**
The method is one that requests a Proxy. Create the proxy by asking the
target MBean for the appropriate ObjectName. The resulting Proxy will implement
the interface given as the return type of the Method.
*/
AMX
invokeSingleProxyGetter(
final Object myProxy,
final Method method,
final Object[] args )
throws IOException, ReflectionException, InstanceNotFoundException, MBeanException,
AttributeNotFoundException
{
// use the methodName as the key for the cache
final String methodName = method.getName();
final int numArgs = (args == null) ? 0 : args.length;
final String argString = args == null ? "" : ArrayStringifier.stringify( args, "_" );
final String cacheKey = methodName + argString;
AMX proxy = getCachedProxy( cacheKey );
if ( proxy == null )
{
final Class returnClass = method.getReturnType();
ObjectName objectName = null;
final String j2eeType = getJ2EEType( returnClass );
if ( numArgs == 0 ) // of the form getXXX() eg getSSLConfig()
{
final String newMethodName = proxyGetterToObjectNameGetter( methodName );
objectName = (ObjectName) invokeTarget( newMethodName, null, EMPTY_SIG);
}
else if ( numArgs == 1 && args[ 0 ].getClass() == String.class )
{
final String newMethodName = proxyGetterToObjectNameGetter( methodName );
objectName = (ObjectName) invokeTarget( newMethodName, args, STRING_SIG );
}
else if ( (methodName.equals( GET_SINGLETON_CONTAINEE ) ||
methodName.equals( GET_CONTAINEE )) && numArgs == 2 )
{
// getContainee( j2eeType, name )
final String newMethodName = proxyGetterToObjectNameGetter( methodName );
objectName = (ObjectName)
invokeTarget( newMethodName, args, GET_SINGLETON_CONTAINEE_OBJECT_NAME_SIG2 );
}
else
{
getProxyLogger().warning( "Unknown form of proxy getter: " + method );
assert( false );
throw new IllegalArgumentException();
}
if ( objectName != null )
{
proxy = createProxy( objectName );
}
// the underlying object may not exist, this occurs normally sometimes
if ( proxy != null )
{
if ( cacheKey != null )
{
//debug( "CACHING: " + cacheKey + " => " + Util.getExtra( proxy ).getObjectName);
cacheProxy( cacheKey, proxy );
}
else
{
//debug( "NOT CACHING: " + Util.getExtra( proxy ).getObjectName);
}
}
else
{
getProxyLogger().fine( "invokeSingleProxyGetter: NULL ObjectName for: " +
methodName + "()" );
}
}
else
{
//debug( "FOUND CACHED using \"" + cacheKey + "\": " + Util.getExtra( proxy ).getObjectName);
}
return( proxy );
}
protected static boolean
isProxyCreator( final Method method )
{
final String methodName = method.getName();
return( methodName.startsWith( CREATE ) &&
AMX.class.isAssignableFrom( method.getReturnType() ) );
}
/**
*/
AMX
invokeProxyCreator(
final Method method,
final Object[] args )
throws IOException, ReflectionException, InstanceNotFoundException, MBeanException,
AttributeNotFoundException
{
final String methodName = method.getName();
final String[] stringSig = getStringSig( method );
final ObjectName objectName = (ObjectName)invokeTarget( methodName, args, stringSig );
assert( objectName != null ) :
"received null ObjectName from: " + methodName + " on target " + getTargetObjectName();
final AMX proxy = createProxy( objectName );
assert( getProxyFactory().getProxy( Util.getExtra( proxy ).getObjectName(),AMX.class, false ) == proxy );
return( proxy );
}
private static String
toString( Object o )
{
String result = o == null ? "null" : SmartStringifier.toString( o );
final int MAX_LENGTH = 256;
if ( result.length() > MAX_LENGTH )
{
result = result.substring( 0, MAX_LENGTH - 1 ) + "...";
}
return result;
}
private static String[]
getStringSig( final Method method )
{
final Class[] sig = method.getParameterTypes();
final String[] stringSig = ClassUtil.classnamesFromSignature( sig );
return( stringSig );
}
protected static String
convertMethodName(
final String srcName,
final String srcSuffix,
final String resultSuffix )
{
if ( ! srcName.endsWith( srcSuffix ) )
{
throw new IllegalArgumentException( srcName + " does not end with " + srcSuffix );
}
final String baseName = srcName.substring( 0, srcName.lastIndexOf( srcSuffix ) );
return( baseName + resultSuffix );
}
private static final Map<String,AMX> EMPTY_String_AMX = Collections.emptyMap();
private Map<String,?>
invokeProxyMapGetter(
final Object myProxy,
final Method method,
final Object[] args )
throws java.io.IOException, ReflectionException, InstanceNotFoundException, MBeanException,
ClassNotFoundException, AttributeNotFoundException, JMException
{
final int argCount = args == null ? 0 : args.length;
// turn getXXXObjectNameMap() into getXXXMap()
final String methodName = method.getName();
final String getObjectNameMapName =
convertMethodName( methodName, MAP_SUFFIX, OBJECT_NAME_MAP_SUFFIX );
final MBeanServerConnection conn = getConnection();
final Map<String,?> m = TypeCast.asMap(
invokeTarget( getObjectNameMapName, args, getStringSig( method ) ) );
assert( m != null ) :
"mbean " + getTargetObjectName() + " returned null Map for " + getObjectNameMapName;
/*
The Map may be either a:
- Map of <name>=<ObjectName>
- Map of <j2eeType>=<Map of <name>=<ObjectName>
*/
Map<String,?> result = null;
if ( m.keySet().size() != 0 )
{
final ProxyFactory proxyFactory = getProxyFactory();
final Object firstValue = m.values().iterator().next();
if ( firstValue instanceof ObjectName )
{
// it's <name>=<ObjectName>
final Map<String,ObjectName> onm = TypeCast.asMap( m );
final Map<String,AMX> proxyMap = proxyFactory.toProxyMap( onm );
result = proxyMap;
}
else if ( firstValue instanceof Map )
{
final Map<String,Map<String,ObjectName>> objectNameMaps = TypeCast.asMap( m );
final Map<String,Map<String,AMX>> proxyMaps = new HashMap<String,Map<String,AMX>>();
for ( final String j2eeType : objectNameMaps.keySet() )
{
final Map<String,ObjectName> objectNameMap = objectNameMaps.get( j2eeType );
final Map<String,AMX> proxyMap = proxyFactory.toProxyMap( objectNameMap );
proxyMaps.put( j2eeType, proxyMap );
}
result = proxyMaps;
}
else
{
throw new IllegalArgumentException();
}
}
else
{
result = EMPTY_String_AMX;
}
return( result );
}
private List<AMX>
invokeProxyListGetter(
final Object myProxy,
final Method method,
final Object[] args )
throws java.io.IOException, ReflectionException, InstanceNotFoundException, MBeanException,
ClassNotFoundException, AttributeNotFoundException, JMException
{
// get the List<ObjectName> from the MBean
final String remoteNAME =
convertMethodName( method.getName(), LIST_SUFFIX, OBJECT_NAME_LIST_SUFFIX );
final List<ObjectName> objectNames = TypeCast.asList(
invokeTarget( remoteNAME, args, getStringSig( method ) ) );
final List<AMX> result = getProxyFactory().toProxyList( objectNames );
return( result );
}
/**
The method is one that requests a Set of Proxies. Create the proxies by asking the
target MBean for the ObjectNames. Then generate proxies of the appropriate type
for each resulting ObjectName.
*/
private Set<AMX>
invokeProxySetGetter(
final Object myProxy,
final Method method,
final Object[] args )
throws java.io.IOException, JMException, ClassNotFoundException
{
assert( Set.class.isAssignableFrom( method.getReturnType() ) );
final String methodName = method.getName();
final String getObjectNamesName =
convertMethodName( methodName, SET_SUFFIX, OBJECT_NAME_SET_SUFFIX );
final MBeanServerConnection conn = getConnection();
final String[] stringSig = getStringSig( method );
// ask the MBean for an ObjectName corresponding to an id (name)
final Set<ObjectName> objectNames = TypeCast.asSet( invokeTarget( getObjectNamesName, args, stringSig ) );
final Set<AMX> proxies = getProxyFactory().toProxySet( objectNames );
return( proxies );
}
private final static Class[] NOTIFICATION_LISTENER_SIG1 = new Class[]
{
NotificationListener.class
};
private final static Class[] NOTIFICATION_LISTENER_SIG2 = new Class[]
{
NotificationListener.class,
NotificationFilter.class,
Object.class
};
private synchronized MBeanInfo
_getMBeanInfo()
throws IOException,
InstanceNotFoundException, ReflectionException, IntrospectionException
{
MBeanInfo mbeanInfo = null;
if ( ! mCheckedForInvariantMBeanInfo )
{
mCheckedForInvariantMBeanInfo = true;
// see if target has the boolean which tells us if caching is OK
try
{
final Boolean cacheIt = (Boolean)
getAttribute( AMXAttributes.ATTR_MBEAN_INFO_IS_INVARIANT );
setMBeanInfoIsInvariant( cacheIt.booleanValue() );
cacheMBeanInfo( cacheIt.booleanValue() );
}
catch( Exception e )
{
// not found, or other problem, have to assume we can't cache it
cacheMBeanInfo( false );
setMBeanInfoIsInvariant( false );
}
}
mbeanInfo = getMBeanInfo( getCacheMBeanInfo() );
return( mbeanInfo );
}
public MBeanInfo
getMBeanInfo()
{
try
{
return( _getMBeanInfo() );
}
catch( Exception e )
{
throw new RuntimeException( e );
}
}
public ObjectName
getObjectName()
{
return( getTargetObjectName() );
}
public Map<String,Object>
getAllAttributes( )
{
Map<String,Object> result = Collections.emptyMap();
try
{
final String[] names = getAttributeNames();
final AttributeList attrs = getAttributes(names );
result = JMXUtil.attributeListToValueMap( attrs );
}
catch( Exception e )
{
throw new RuntimeException( e );
}
return( result );
}
public String[]
getAttributeNames()
{
final String attrName = "AttributeNames";
Attribute attr = null;
try
{
attr = getCachedAttribute( attrName );
}
catch( AttributeNotFoundException e )
{
// it's supposed to be there!
attr = null;
}
catch( Exception e )
{
throw new RuntimeException( e );
}
String[] names = null;
if ( attr == null )
{
final MBeanInfo mbeanInfo = getMBeanInfo();
names = JMXUtil.getAttributeNames( mbeanInfo.getAttributes() );
if ( getMBeanInfoIsInvariant() )
{
// only cache if MBeanInfo is invariant
cacheAttribute( new Attribute( attrName, names ) );
}
}
else
{
names = (String[])attr.getValue();
}
return( names );
}
/**
The values of these Attributes are cached forever. Proxies are handled separately
because the API will be getXXX() wherease the Attribute name will be XXXObjectName.
*/
private static final Set<String> CACHED_ATTRIBUTE_NAMES = GSetUtil.newUnmodifiableStringSet(
AMXAttributes.ATTR_MBEAN_INFO_IS_INVARIANT,
AMXAttributes.ATTR_INTERFACE_NAME,
AMXAttributes.ATTR_GROUP,
AMXAttributes.ATTR_FULL_TYPE,
CONTAINEE_J2EE_TYPES );
private static final String GET_MBEAN_INFO = GET + MBEAN_INFO;
private static final String GET_J2EE_TYPE = GET + J2EE_TYPE;
private static final String GET_J2EE_NAME = GET + J2EE_NAME;
private static final String GET_ATTRIBUTE_NAMES = GET + ATTRIBUTE_NAMES;
private static final String GET_CONTAINER = GET + CONTAINER;
private static final String GET_EXTRA = GET + "Extra";
private static final String GET_ALL_ATTRIBUTES = GET + "AllAttributes";
private static final String GET_DOMAIN_ROOT = GET + DOMAIN_ROOT;
private static final String GET_OBJECT_NAME = GET + AMXAttributes.ATTR_OBJECT_NAME;
/**
These Attributes are handled specially. For example, J2EE_TYPE and
J2EE_NAME are part of the ObjectName.
*/
private static final Set<String> SPECIAL_METHOD_NAMES = GSetUtil.newUnmodifiableStringSet(
GET_MBEAN_INFO,
GET_J2EE_TYPE,
GET_J2EE_NAME,
GET_ATTRIBUTE_NAMES,
GET_CONTAINER,
GET_DOMAIN_ROOT,
GET_OBJECT_NAME,
GET_EXTRA,
GET_ALL_ATTRIBUTES,
ADD_NOTIFICATION_LISTENER,
REMOVE_NOTIFICATION_LISTENER
);
/**
Handle a "special" method; one that requires special handling and/or can
be dealt with on the client side and/or can be handled most efficiently
by special-casing it.
*/
private Object
handleSpecialMethod(
final Object myProxy,
final Method method,
final Object[] args )
throws ClassNotFoundException, JMException, IOException
{
final String methodName = method.getName();
final int numArgs = args == null ? 0 : args.length;
Object result = null;
boolean handled = true;
if ( numArgs == 0 )
{
if ( methodName.equals( GET_CONTAINER ) )
{
result = getContainer( Util.asAMX(myProxy) );
}
else if ( methodName.equals( GET_EXTRA ) )
{
assert( this instanceof Extra );
result = this;
}
else if ( methodName.equals( GET_OBJECT_NAME ) )
{
result = getTargetObjectName();
}
else if ( methodName.equals( GET_DOMAIN_ROOT ) )
{
result = getDomainRoot( );
}
else if ( methodName.equals( GET_ATTRIBUTE_NAMES ) )
{
result = getAttributeNames();
}
else if ( methodName.equals( GET_J2EE_TYPE ) )
{
result = Util.getJ2EEType( getTargetObjectName() );
}
else if ( methodName.equals( GET_J2EE_NAME ) )
{
result = Util.getName( getTargetObjectName() );
}
else if ( methodName.equals( GET_ALL_ATTRIBUTES ) )
{
result = getAllAttributes();
}
else
{
handled = false;
}
}
else if ( numArgs == 1 && methodName.equals( "equals" ) )
{
return equals( args[ 0 ] );
}
else
{
final Class[] signature = method.getParameterTypes();
if ( methodName.equals( ADD_NOTIFICATION_LISTENER ) &&
( ClassUtil.sigsEqual( NOTIFICATION_LISTENER_SIG1, signature ) ||
ClassUtil.sigsEqual( NOTIFICATION_LISTENER_SIG2, signature ) )
)
{
addNotificationListener( args );
}
else if ( methodName.equals( REMOVE_NOTIFICATION_LISTENER ) &&
( ClassUtil.sigsEqual( NOTIFICATION_LISTENER_SIG1, signature ) ||
ClassUtil.sigsEqual( NOTIFICATION_LISTENER_SIG2, signature ) )
)
{
removeNotificationListener( args );
}
else
{
handled = false;
}
}
if ( ! handled )
{
assert( false );
throw new RuntimeException( "unknown method: " + method );
}
return( result );
}
public final Object
invoke(
final Object myProxy,
final Method method,
final Object[] args )
throws java.lang.Throwable
{
try
{
final Object result = _invoke( myProxy, method, args );
assert( result == null ||
ClassUtil.IsPrimitiveClass( method.getReturnType() ) ||
method.getReturnType().isAssignableFrom( result.getClass() ) ) :
method.getName() + ": result of type " + result.getClass().getName() +
" not assignable to " + method.getReturnType().getName() + ", " +
"interfaces: " + toString( result.getClass().getInterfaces() +
", ObjectName = " + JMXUtil.toString( getTargetObjectName() ) );
return result;
}
catch( IOException e )
{
getProxyFactory().checkConnection();
throw e;
}
catch( InstanceNotFoundException e )
{
checkValid();
throw e;
}
}
protected Object
_invoke(
final Object myProxy,
final Method method,
final Object[] args )
throws java.lang.Throwable
{
debugMethod( method.getName(), args );
// clients can retain proxies that go invalid if their corresponding
// MBeans are removed.
if ( ! targetIsValid() )
{
throw new InstanceNotFoundException( getTargetObjectName().toString() );
}
Object result = null;
final String methodName = method.getName();
final int numArgs = args == null ? 0 : args.length;
boolean handled = false;
if ( SPECIAL_METHOD_NAMES.contains( methodName ) )
{
handled = true;
result = handleSpecialMethod( myProxy, method, args );
}
else if ( JMXUtil.isIsOrGetter( method ) )
{
assert( ! handled );
final String attrName = JMXUtil.getAttributeName( method );
if ( CACHED_ATTRIBUTE_NAMES.contains( attrName ) )
{
result = getCachedAttributeValue( attrName );
handled = true;
}
}
if ( ! handled )
{
if ( isSingleProxyGetter( method, numArgs) )
{
result = invokeSingleProxyGetter( myProxy, method, args );
}
else if ( isProxySetGetter( method, numArgs ) )
{
result = invokeProxySetGetter( myProxy, method, args );
}
else if ( isProxyMapGetter( method, numArgs ) )
{
result = invokeProxyMapGetter( myProxy, method, args );
}
else if ( isProxyListGetter( method, numArgs ) )
{
result = invokeProxyListGetter( myProxy, method, args );
}
else if ( isProxyCreator( method ) )
{
result = invokeProxyCreator( method, args );
}
else
{
result = super.invoke( myProxy, method, args );
}
}
if ( getDebug() )
{
debug( AMXDebug.methodString( methodName, args ) +
" => " + toString( result ) );
}
return( result );
}
protected void
addNotificationListener( final Object[] args )
throws IOException, InstanceNotFoundException
{
final NotificationListener listener = (NotificationListener)args[ 0 ];
final NotificationFilter filter = (NotificationFilter)(args.length <= 1 ? null : args[ 1 ]);
final Object handback = args.length <= 1 ? null : args[ 2 ];
getConnection().addNotificationListener(
getTargetObjectName(), listener, filter, handback );
}
protected void
removeNotificationListener( final Object[] args )
throws IOException, InstanceNotFoundException, ListenerNotFoundException
{
final NotificationListener listener = (NotificationListener)args[ 0 ];
// important:
// this form removes the same listener registered with different filters and/or handbacks
if ( args.length == 1 )
{
getConnection().removeNotificationListener( getTargetObjectName(), listener );
}
else
{
final NotificationFilter filter = (NotificationFilter)args[ 1 ];
final Object handback = args[ 2 ];
getConnection().removeNotificationListener(
getTargetObjectName(), listener, filter, handback );
}
}
}