/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.jmx.namespace;
import com.sun.jmx.defaults.JmxProperties;
import com.sun.jmx.mbeanserver.Util;
import java.io.IOException;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.InvalidAttributeValueException;
import javax.management.JMRuntimeException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServerConnection;
import javax.management.NotCompliantMBeanException;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.QueryExp;
import javax.management.ReflectionException;
import javax.management.RuntimeMBeanException;
import javax.management.RuntimeOperationsException;
/**
* A RoutingMBeanServerConnection wraps a MBeanServerConnection, defining
* abstract methods that can be implemented by subclasses to rewrite
* routing ObjectNames. It is used to implement
* HandlerInterceptors (wrapping JMXNamespace instances) and routing
* proxies (used to implement cd operations).
*
* This API is a Sun internal API and is subject to changes without notice.
*
* @since 1.7
*/
public abstract class RoutingMBeanServerConnection
implements MBeanServerConnection {
/**
* A logger for this class.
**/
private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
/**
* Creates a new instance of RoutingMBeanServerConnection
*/
public RoutingMBeanServerConnection() {
}
/**
* Returns the wrapped source connection. The {@code source} connection
* is a connection to the MBeanServer that contains the actual MBean.
* In the case of cascading, that would be a connection to the sub
* agent.
**/
protected abstract T source() throws IOException;
/**
* Converts a target ObjectName to a source ObjectName.
* The target ObjectName is the name of the MBean in the mount point
* target. In the case of cascading, that would be the name of the
* MBean in the master agent. So if a subagent S containing an MBean
* named "X" is mounted in the target namespace "foo//" of a master agent M,
* the source is S, the target is "foo//" in M, the source name is "X", and
* the target name is "foo//X".
* In the case of cascading - such as in NamespaceInterceptor, this method
* will convert "foo//X" (the targetName) into "X", the source name.
* @throws IllegalArgumentException if the name cannot be converted.
**/
protected abstract ObjectName toSource(ObjectName targetName);
/**
* Converts a source ObjectName to a target ObjectName.
* (see description of toSource above for explanations)
* In the case of cascading - such as in NamespaceInterceptor, this method
* will convert "X" (the sourceName) into "foo//X", the target name.
* @throws IllegalArgumentException if the name cannot be converted.
**/
protected abstract ObjectName toTarget(ObjectName sourceName);
/**
* Can be overridden by subclasses to check the validity of a new
* ObjectName used in createMBean or registerMBean.
* This method is typically used by subclasses which might require
* special handling for "null";
**/
protected ObjectName newSourceMBeanName(ObjectName targetName)
throws MBeanRegistrationException {
try {
return toSource(targetName);
} catch (Exception x) {
throw new MBeanRegistrationException(x,"Illegal MBean Name");
}
}
// Calls toSource(), Wraps IllegalArgumentException.
ObjectName toSourceOrRuntime(ObjectName targetName) {
try {
return toSource(targetName);
} catch (RuntimeException x) {
throw makeCompliantRuntimeException(x);
}
}
// Wraps given exception if needed.
RuntimeException makeCompliantRuntimeException(Exception x) {
if (x instanceof SecurityException) return (SecurityException)x;
if (x instanceof JMRuntimeException) return (JMRuntimeException)x;
if (x instanceof RuntimeException)
return new RuntimeOperationsException((RuntimeException)x);
if (x instanceof IOException)
return Util.newRuntimeIOException((IOException)x);
// shouldn't come here...
final RuntimeException x2 = new UndeclaredThrowableException(x);
return new RuntimeOperationsException(x2);
}
// from MBeanServerConnection
public AttributeList getAttributes(ObjectName name, String[] attributes)
throws InstanceNotFoundException, ReflectionException, IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
try {
return source().getAttributes(sourceName, attributes);
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public Object invoke(ObjectName name, String operationName, Object[] params,
String[] signature)
throws InstanceNotFoundException, MBeanException, ReflectionException,
IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
try {
final Object result =
source().invoke(sourceName,operationName,params,
signature);
return result;
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public void unregisterMBean(ObjectName name)
throws InstanceNotFoundException, MBeanRegistrationException,
IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
try {
source().unregisterMBean(sourceName);
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public MBeanInfo getMBeanInfo(ObjectName name)
throws InstanceNotFoundException, IntrospectionException,
ReflectionException, IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
try {
return source().getMBeanInfo(sourceName);
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public ObjectInstance getObjectInstance(ObjectName name)
throws InstanceNotFoundException, IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
try {
return processOutputInstance(
source().getObjectInstance(sourceName));
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public boolean isRegistered(ObjectName name) throws IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
try {
return source().isRegistered(sourceName);
} catch (RuntimeMBeanException x) {
throw new RuntimeOperationsException(x.getTargetException());
} catch (RuntimeException x) {
throw makeCompliantRuntimeException(x);
}
}
// from MBeanServerConnection
public void setAttribute(ObjectName name, Attribute attribute)
throws InstanceNotFoundException, AttributeNotFoundException,
InvalidAttributeValueException, MBeanException,
ReflectionException, IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
try {
source().setAttribute(sourceName,attribute);
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public ObjectInstance createMBean(String className,
ObjectName name, ObjectName loaderName,
Object[] params, String[] signature)
throws ReflectionException, InstanceAlreadyExistsException,
MBeanRegistrationException, MBeanException,
NotCompliantMBeanException, InstanceNotFoundException, IOException {
final ObjectName sourceName = newSourceMBeanName(name);
// Loader Name is already a sourceLoaderName.
final ObjectName sourceLoaderName = loaderName;
try {
final ObjectInstance instance =
source().createMBean(className,sourceName,
sourceLoaderName,
params,signature);
return processOutputInstance(instance);
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public ObjectInstance createMBean(String className, ObjectName name,
Object[] params, String[] signature)
throws ReflectionException, InstanceAlreadyExistsException,
MBeanRegistrationException, MBeanException,
NotCompliantMBeanException, IOException {
final ObjectName sourceName = newSourceMBeanName(name);
try {
return processOutputInstance(source().createMBean(className,
sourceName,params,signature));
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public ObjectInstance createMBean(String className, ObjectName name,
ObjectName loaderName)
throws ReflectionException, InstanceAlreadyExistsException,
MBeanRegistrationException, MBeanException,
NotCompliantMBeanException, InstanceNotFoundException, IOException {
final ObjectName sourceName = newSourceMBeanName(name);
// Loader Name is already a source Loader Name.
final ObjectName sourceLoaderName = loaderName;
try {
return processOutputInstance(source().createMBean(className,
sourceName,sourceLoaderName));
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public ObjectInstance createMBean(String className, ObjectName name)
throws ReflectionException, InstanceAlreadyExistsException,
MBeanRegistrationException, MBeanException,
NotCompliantMBeanException, IOException {
final ObjectName sourceName = newSourceMBeanName(name);
try {
return processOutputInstance(source().
createMBean(className,sourceName));
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public Object getAttribute(ObjectName name, String attribute)
throws MBeanException, AttributeNotFoundException,
InstanceNotFoundException, ReflectionException, IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
try {
return source().getAttribute(sourceName,attribute);
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public boolean isInstanceOf(ObjectName name, String className)
throws InstanceNotFoundException, IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
try {
return source().isInstanceOf(sourceName,className);
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public AttributeList setAttributes(ObjectName name, AttributeList attributes)
throws InstanceNotFoundException, ReflectionException, IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
try {
return source().
setAttributes(sourceName,attributes);
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// Return names in the target's context.
Set processOutputInstances(Set sources) {
final Set result = Util.equivalentEmptySet(sources);
for (ObjectInstance i : sources) {
try {
final ObjectInstance target = processOutputInstance(i);
if (excludesFromResult(target.getObjectName(), "queryMBeans"))
continue;
result.add(target);
} catch (Exception x) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("Skiping returned item: " +
"Unexpected exception while processing " +
"ObjectInstance: " + x);
}
continue;
}
}
return result;
}
// Return names in the target's context.
ObjectInstance processOutputInstance(ObjectInstance source) {
if (source == null) return null;
final ObjectName sourceName = source.getObjectName();
try {
final ObjectName targetName = toTarget(sourceName);
return new ObjectInstance(targetName,source.getClassName());
} catch (RuntimeException x) {
throw makeCompliantRuntimeException(x);
}
}
// Returns names in the target's context.
Set processOutputNames(Set sourceNames) {
final Set names = Util.equivalentEmptySet(sourceNames);
for (ObjectName n : sourceNames) {
try {
final ObjectName targetName = toTarget(n);
if (excludesFromResult(targetName, "queryNames")) continue;
names.add(targetName);
} catch (Exception x) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("Skiping returned item: " +
"Unexpected exception while processing " +
"ObjectInstance: " + x);
}
continue;
}
}
return names;
}
// from MBeanServerConnection
public Set queryMBeans(ObjectName name,
QueryExp query) throws IOException {
if (name == null) name=ObjectName.WILDCARD;
final ObjectName sourceName = toSourceOrRuntime(name);
try {
return processOutputInstances(
source().queryMBeans(sourceName,query));
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public Set queryNames(ObjectName name, QueryExp query)
throws IOException {
if (name == null) name=ObjectName.WILDCARD;
final ObjectName sourceName = toSourceOrRuntime(name);
try {
final Set tmp = source().queryNames(sourceName,query);
final Set out = processOutputNames(tmp);
//System.err.println("queryNames: out: "+out);
return out;
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public void removeNotificationListener(ObjectName name,
NotificationListener listener)
throws InstanceNotFoundException,
ListenerNotFoundException, IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
try {
source().removeNotificationListener(sourceName,listener);
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public void addNotificationListener(ObjectName name, ObjectName listener,
NotificationFilter filter, Object handback)
throws InstanceNotFoundException, IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
// Listener name is already a source listener name.
try {
source().addNotificationListener(sourceName,listener,
filter,handback);
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public void addNotificationListener(ObjectName name,
NotificationListener listener, NotificationFilter filter,
Object handback) throws InstanceNotFoundException, IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
try {
source().addNotificationListener(sourceName, listener, filter,
handback);
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public void removeNotificationListener(ObjectName name,
NotificationListener listener, NotificationFilter filter,
Object handback)
throws InstanceNotFoundException, ListenerNotFoundException,
IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
try {
source().removeNotificationListener(sourceName,listener,filter,
handback);
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public void removeNotificationListener(ObjectName name, ObjectName listener,
NotificationFilter filter, Object handback)
throws InstanceNotFoundException, ListenerNotFoundException,
IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
try {
source().removeNotificationListener(sourceName,listener,
filter,handback);
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public void removeNotificationListener(ObjectName name, ObjectName listener)
throws InstanceNotFoundException, ListenerNotFoundException,
IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
// listener name is already a source name...
final ObjectName sourceListener = listener;
try {
source().removeNotificationListener(sourceName,sourceListener);
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public Integer getMBeanCount() throws IOException {
try {
return source().getMBeanCount();
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public String[] getDomains() throws IOException {
try {
return source().getDomains();
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public String getDefaultDomain() throws IOException {
try {
return source().getDefaultDomain();
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
/**
* Returns true if the given targetName must be excluded from the
* query result.
* In this base class, always return {@code false}.
* By default all object names returned by the sources are
* transmitted to the caller - there is no filtering.
*
* @param name A target object name expressed in the caller's
* context. In the case of cascading, where the source
* is a sub agent mounted on e.g. namespace "foo",
* that would be a name prefixed by "foo//"...
* @param queryMethod either "queryNames" or "queryMBeans".
* @return true if the name must be excluded.
*/
boolean excludesFromResult(ObjectName targetName, String queryMethod) {
return false;
}
}