![JAR search and dependency download from the Maven repository](/logo.png)
org.glassfish.admin.amx.impl.config.AMXConfigImpl Maven / Gradle / Ivy
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2011-2015 Oracle and/or its affiliates. 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_1_1.html
* or packager/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 packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [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.
*/
// Portions Copyright [2016-2020] Payara Foundation and/or affiliates
package org.glassfish.admin.amx.impl.config;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyVetoException;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.*;
import javax.management.Attribute;
import static org.glassfish.admin.amx.config.AMXConfigConstants.*;
import org.glassfish.admin.amx.config.AMXConfigProxy;
import org.glassfish.admin.amx.config.AttributeResolver;
import org.glassfish.admin.amx.core.AMXProxy;
import org.glassfish.admin.amx.core.Util;
import org.glassfish.admin.amx.impl.mbean.AMXImplBase;
import org.glassfish.admin.amx.impl.util.*;
import org.glassfish.admin.amx.util.*;
import org.glassfish.admin.amx.util.jmx.JMXUtil;
import static org.glassfish.external.amx.AMX.*;
import org.glassfish.external.arc.Stability;
import org.glassfish.external.arc.Taxonomy;
import org.jvnet.hk2.config.*;
/**
* Base class from which all AMX Config MBeans should derive (but not "must").
*
*/
@Taxonomy(stability = Stability.NOT_AN_INTERFACE)
public class AMXConfigImpl extends AMXImplBase {
private final ConfigBean mConfigBean;
private static final Logger logger = AMXLoggerInfo.getLogger();
/**
* MBeanInfo derived from the AMXConfigProxy interface, always the same
*/
private static MBeanInfo configMBeanInfo;
private static synchronized MBeanInfo getAMXConfigMBeanInfo() {
if (configMBeanInfo == null) {
configMBeanInfo = MBeanInfoSupport.getMBeanInfo(AMXConfigProxy.class);
}
return configMBeanInfo;
}
/**
* We save time and space by creating exactly one MBeanInfo for any given config interface; it can be shared among all instances since it is invariant.
*/
private static final ConcurrentMap, MBeanInfo> mInfos
= new ConcurrentHashMap, MBeanInfo>();
private static MBeanInfo createMBeanInfo(final ConfigBean cb) {
Class extends ConfigBeanProxy> intf = cb.getProxyType();
MBeanInfo newInfo = mInfos.get(intf);
if (newInfo != null) {
return newInfo;
}
final ConfigBeanJMXSupport spt = ConfigBeanJMXSupportRegistry.getInstance(cb);
final MBeanInfo info = spt.getMBeanInfo();
final List attrInfos = ListUtil.newListFromArray(info.getAttributes());
final MBeanInfo spiInfo = MBeanInfoSupport.getAMX_SPIMBeanInfo();
// make a list so we can remove "Children" attribute if this MBean cannot have any
final List spiAttrInfos = ListUtil.newListFromArray(spiInfo.getAttributes());
if (spt.isLeaf()) {
JMXUtil.remove(spiAttrInfos, ATTR_CHILDREN);
}
// Add in the AMX_SPI attributes, replacing any with the same name
for (final MBeanAttributeInfo attrInfo : spiAttrInfos) {
// remove existing info
final String attrName = attrInfo.getName();
final MBeanAttributeInfo priorAttrInfo = JMXUtil.remove(attrInfos, attrName);
// special case the Name attribute to preserve its metadata
if (attrName.equals(ATTR_NAME) && priorAttrInfo != null) {
final Descriptor mergedD = JMXUtil.mergeDescriptors(attrInfo.getDescriptor(), priorAttrInfo.getDescriptor());
final MBeanAttributeInfo newAttrInfo = new MBeanAttributeInfo(attrName,
attrInfo.getType(), attrInfo.getDescription(), attrInfo.isReadable(), attrInfo.isWritable(), attrInfo.isIs(), mergedD);
attrInfos.add(newAttrInfo);
} else {
attrInfos.add(attrInfo);
}
}
final List operationInfos = ListUtil.newListFromArray(info.getOperations());
operationInfos.addAll(ListUtil.newListFromArray(getAMXConfigMBeanInfo().getOperations()));
final MBeanAttributeInfo[] attrs = new MBeanAttributeInfo[attrInfos.size()];
attrInfos.toArray(attrs);
final MBeanOperationInfo[] operations = new MBeanOperationInfo[operationInfos.size()];
operationInfos.toArray(operations);
newInfo = new MBeanInfo(
info.getClassName(),
info.getDescription(),
attrs,
info.getConstructors(),
operations,
info.getNotifications(),
info.getDescriptor());
MBeanInfo oldInfo = mInfos.putIfAbsent(intf, newInfo);
return oldInfo != null ? oldInfo : newInfo;
}
public AMXConfigImpl(
final ObjectName parentObjectName,
final ConfigBean configBean) {
super(parentObjectName, createMBeanInfo(configBean));
mConfigBean = configBean;
// eager initialization, it will be needed momentarily
getConfigBeanJMXSupport();
}
@Override
protected void setAttributeManually(final Attribute attr)
throws AttributeNotFoundException, InvalidAttributeValueException {
final AttributeList attrList = new AttributeList();
attrList.add(attr);
try {
final AttributeList successList = setAttributesInConfigBean(attrList);
if (successList.isEmpty()) {
throw new AttributeNotFoundException(attr.getName());
}
} catch (final Exception e) {
// propogate the stack trace back, it's important for clients to have somethingto go on
final Throwable rootCause = ExceptionUtil.getRootCause(e);
throw new AttributeNotFoundException(ExceptionUtil.toString(rootCause));
}
}
/**
* Note that the default implementation sets attributes one at a time, but that MBeans with transactional requirements (eg configuration) may wish to set
* them as a group.
*/
@Override
public AttributeList setAttributes(final AttributeList attrs) {
try {
return setAttributesTransactionally(attrs);
} catch (final Exception e) {
// squelch, per JMX spec
}
// return an empty list, per JMX spec for failure
return new AttributeList();
}
public AttributeList setAttributesTransactionally(final AttributeList attrs) throws Exception {
final AttributeList successList = new AttributeList();
try {
final AttributeList delegateSuccess = setAttributesInConfigBean(attrs);
successList.addAll(delegateSuccess);
} catch (final Exception e) {
// propogate the stack trace back, it's important for clients to have something to go on
final Throwable rootCause = ExceptionUtil.getRootCause(e);
// do not propagate back any proprietary exception; class might not exist on client
throw new Exception(ExceptionUtil.toString(rootCause));
}
return successList;
}
/**
* The actual name could be different than the 'name' property in the ObjectName if it contains characters that are illegal for an ObjectName. Also, there
* can be a Name attribute which is not a key value.
*/
@Override
public String getName() {
final ConfigBean cb = getConfigBean();
String name = AMXConfigLoader.getKey(cb);
if (name == null) {
// deal with annoying and rare case of name existing, but not a key value
name = cb.rawAttribute("name");
}
return name == null ? NO_NAME : name;
}
private ConfigBean getConfigBean() {
return mConfigBean;
}
private ConfigBeanProxy getConfigBeanProxy() {
return getConfigBean().getProxy(getConfigBean().getProxyType());
}
/**
* Resolve a template String. See {@link AttributeResolver} for details.
*/
public String resolveAttributeValue(final String varString) {
if (!AttributeResolverHelper.needsResolving(varString)) {
return varString;
}
return new AttributeResolverHelper().resolve(varString);
}
public String resolveAttribute(final String attrName) {
try {
final Object value = getAttribute(attrName);
return resolveAttributeValue(value == null ? null : "" + value);
} catch (final AttributeNotFoundException e) {
logger.log(Level.SEVERE, AMXLoggerInfo.attributeNotfound, new Object[]{attrName, getObjectName()});
return null;
}
}
public Boolean resolveBoolean(final String attrName) {
return Boolean.parseBoolean(resolveAttribute(attrName));
}
public Integer resolveInteger(final String attrName) {
return Integer.parseInt(resolveAttribute(attrName));
}
public Long resolveLong(final String attrName) {
return Long.parseLong(resolveAttribute(attrName));
}
public AttributeList resolveAttributes(final String[] attrNames) {
Issues.getAMXIssues().notDone("resolveAttributes: use annotations to create the correct type");
final AttributeList attrs = getAttributes(attrNames);
final AttributeList resolvedAttrs = new AttributeList();
for (final Object o : attrs) {
Attribute r = (Attribute) o;
// allow non-String attributes
final Object value = r.getValue();
if ((value instanceof String) && AttributeResolverHelper.needsResolving((String) value)) {
final String resolvedValue = resolveAttributeValue((String) value);
// TODO: use annotation to determine correct type
r = new Attribute(r.getName(), resolvedValue);
}
resolvedAttrs.add(r);
}
return resolvedAttrs;
}
//========================================================================================
/**
* Parameters for creating one or more children, each of which can (recursively) contain other descendants.
*/
static class CreateParams {
final String mType;
final Map mAttrs;
final List mChildren;
public CreateParams(final String type, final Map values) {
mAttrs = MapUtil.newMap();
mChildren = ListUtil.newList();
mType = type;
if (values == null) {
return; // null is legal, no attributes
}
for (final Map.Entry me : values.entrySet()) {
final String nameAsProvided = me.getKey();
final String xmlName = ConfigBeanJMXSupport.toXMLName(nameAsProvided); // or type
final Object value = me.getValue();
if (value == null
|| (value instanceof String)
|| (value instanceof Number)
|| (value instanceof Boolean)) {
// auto-convert specific basic types to String
final String valueString = value == null ? null : "" + value;
mAttrs.put(xmlName, valueString);
} else if (value instanceof String[]) {
// A String[] is always mapped to a List
mAttrs.put(xmlName, ListUtil.asStringList(value));
} else if (value instanceof Map) {
// one sub-element whose type is its key in the containing Map
final Map m = TypeCast.checkMap(Map.class.cast(value), String.class, Object.class);
final CreateParams child = new CreateParams(xmlName, m);
mChildren.add(child);
} else if (value instanceof Map[]) {
// one or more sub elements whose type is its key in the containing Map
final Map[] maps = (Map[]) value;
for (final Map m : maps) {
final Map mTyped = TypeCast.checkMap(m, String.class, Object.class);
final CreateParams child = new CreateParams(xmlName, mTyped);
mChildren.add(child);
}
} else {
throw new IllegalArgumentException("Value of class " + value.getClass().getName() + " not supported for attribute " + nameAsProvided);
}
}
}
public String type() { return mType; }
public String name() { return (String)mAttrs.get("name"); }
public Map attrs() { return Collections.unmodifiableMap(mAttrs); }
public List children() { return Collections.unmodifiableList(mChildren); }
/**
* Convert incoming attributes to HK2 requirements.
*/
List
toAttributeChanges(final Map values) {
if (values == null) {
return null;
}
final List changes = ListUtil.newList();
for (Map.Entry xmlEntry : mAttrs.entrySet()) {
final Object value = xmlEntry.getValue();
if (value instanceof String) {
changes.add(new ConfigSupport.SingleAttributeChange(xmlEntry.getKey(), (String) value));
} else {
// what about String[]?
throw new IllegalArgumentException();
}
}
return changes;
}
public String toString(final String prefix) {
final StringBuilder buf = new StringBuilder();
final String NL = StringUtil.LS;
// crude toString, really should indent
buf.append(prefix).append(mType).append(" = ").append(mAttrs).append(NL);
if (!mChildren.isEmpty()) {
buf.append(prefix).append("[");
for (final CreateParams child : mChildren) {
buf.append(child.toString(" " + prefix)).append(NL);
}
buf.append(prefix).append(']');
}
return buf.toString();
}
@Override
public String toString() {
return toString("");
}
}
/**
* To make error messages more friendly and quick sanity check, verify that no conflicting children already exist.
*/
private void
checkForConflicts(final List children) {
final Map> existingChildren = getSelf().childrenMaps();
for (final CreateParams params : children) {
final String type = params.type();
final Map childrenOfType = existingChildren.get(type);
if (childrenOfType != null) {
// children of this type exist, check that there is no conflicting child already
final AMXProxy firstChild = childrenOfType.values().iterator().next();
if (firstChild.extra().singleton()) {
throw new IllegalArgumentException("Singleton child of type " + type + " already exists.");
}
if (childrenOfType.get(params.name()) != null) {
throw new IllegalArgumentException("Child of type " + type + " named " + params.name() + " already exists.");
}
}
}
}
ObjectName[] createChildren(final List children, final Map attrs) {
debug(children.toString());
checkForConflicts(children);
final ConfigBeanProxy parent = getConfigBeanProxy();
final ChildrenCreator creator = new ChildrenCreator(children, attrs);
try {
ConfigSupport.apply(creator, parent);
} catch (Exception e) {
AMXLoggerInfo.getLogger().log(Level.INFO, AMXLoggerInfo.cantCreateChildren, e);
throw new RuntimeException(e);
}
// ensure that all new ConfigBeans have been registered as MBeans
final List newMBeans = ListUtil.newList();
final List newDescendants = creator.configBeans();
final AMXConfigLoader amxLoader = SingletonEnforcer.get(AMXConfigLoader.class);
for (final ConfigBean newDescendant : newDescendants) {
amxLoader.handleConfigBean(newDescendant, true);
final ObjectName objectName = ConfigBeanRegistry.getInstance().getObjectName(newDescendant);
newMBeans.add(objectName);
}
return CollectionUtil.toArray(newMBeans, ObjectName.class);
}
public ObjectName[]
createChildren(
final Map[]> childrenMaps,
final Map attrs) {
final List children = ListUtil.newList();
for (Map.Entry[]> entry : childrenMaps.entrySet()) {
for (final Map m : entry.getValue()) {
children.add(new CreateParams(entry.getKey(), m));
}
}
return createChildren(children, attrs);
}
/**
* Create one or more children
*/
private final class ChildrenCreator implements ConfigCode {
private final List mChildrenMaps;
private final Map mAttrs;
private final List mNewConfigBeans;
ChildrenCreator(final List childrenMaps, final Map attrs) {
mChildrenMaps = childrenMaps;
mAttrs = attrs;
mNewConfigBeans = ListUtil.newList();
}
@Override
public Object run(final ConfigBeanProxy... params)
throws PropertyVetoException, TransactionFailure {
if (params.length != 1) {
throw new IllegalArgumentException();
}
final ConfigBeanProxy parent = params[0];
final ConfigBean source = (ConfigBean) ConfigBean.unwrap(parent);
final ConfigSupport configSupport = source.getHabitat().getService(ConfigSupport.class);
return _run(parent, configSupport);
}
public Object _run(
final ConfigBeanProxy parent,
final ConfigSupport configSupport)
throws PropertyVetoException, TransactionFailure {
final WriteableView parentW = WriteableView.class.cast(Proxy.getInvocationHandler(Proxy.class.cast(parent)));
// if attributes were specified, set them first.
if (mAttrs != null) {
setAttrs(parent, mAttrs);
}
final SubElementsCallback callback = new SubElementsCallback(mChildrenMaps);
final ConfigBeanJMXSupport sptRoot = ConfigBeanJMXSupportRegistry.getInstance(Dom.unwrap(parent).getProxyType());
final List newDescendants = callback.recursiveCreate(parentW, sptRoot, mChildrenMaps);
mNewConfigBeans.addAll(newDescendants);
return null;
}
public List configBeans() {
return mNewConfigBeans;
}
}
public ObjectName createChild(final String type, final Map params) {
final CreateParams childParams = new CreateParams(type, params);
final List children = ListUtil.newList();
children.add(childParams);
final ObjectName[] objectNames = createChildren(children, null);
return objectNames[0];
}
/**
* Replace "Name" or "name" with the
*/
Map
replaceNameWithKey(
final Map attrs,
final ConfigBeanJMXSupport spt) {
String key = null;
if (attrs.containsKey(ATTR_NAME)) {
key = ATTR_NAME;
} else if (attrs.containsKey("name")) {
key = "name";
}
Map m = attrs;
if (key != null) {
// map "Name" or "name" to the actual key value (which could be "name')
final String xmlKeyName = spt.getNameHint();
// rename to the appropriate key name, if it doesn't already exist
// eg there could be a non-key attribute "Name" and another key attribute; leave that alone
if (xmlKeyName != null && !attrs.keySet().contains(xmlKeyName)) {
m = new HashMap(attrs);
final Object value = m.remove(key);
m.put(xmlKeyName, value);
}
}
return m;
}
/**
* exists so we can get the parameterized return type
*/
public static List listOfString() {
return null;
}
public static String convertAttributeName(final String s) {
// do not alter any name that is already all lower-case or that contains a "-" */
if (s.equals(s.toLowerCase(Locale.getDefault())) || s.indexOf('-') >= 0) {
return (s);
}
// Dom.convertName() has a bug: IsFooBar => is-foo-bar, but is-foo-bar => -foo-bar.
return Dom.convertName(s);
}
private void setAttrs(
final ConfigBeanProxy target,
final Map attrs) {
final WriteableView targetW = WriteableView.class.cast(Proxy.getInvocationHandler(Proxy.class.cast(target)));
for (final Map.Entry me : attrs.entrySet()) {
final String attrName = me.getKey();
final Object attrValue = me.getValue();
final String xmlName = convertAttributeName(attrName);
final ConfigBean targetCB = (ConfigBean) Dom.unwrap(target);
final ConfigModel.Property modelProp = targetCB.model.findIgnoreCase(xmlName);
if (modelProp == null) {
throw new IllegalArgumentException("Can't find ConfigModel.Property for attr " + xmlName + " on " + targetCB.getProxyType());
}
if (modelProp.isCollection()) {
java.lang.reflect.Method method;
try {
method = getClass().getMethod("listOfString", new Class[0]);
} catch (final NoSuchMethodException | SecurityException e) {
throw new IllegalStateException("impossible");
}
final java.lang.reflect.Type listOfStringClass = method.getGenericReturnType();
List list;
if (attrValue instanceof String[]) {
list = ListUtil.asStringList(attrValue);
} else {
list = TypeCast.checkList(TypeCast.asList(attrValue), String.class);
}
targetW.setter(modelProp, list, listOfStringClass);
} else {
targetW.setter(modelProp, attrValue, String.class);
}
}
}
/**
* Callback to create sub-elements (recursively) on a newly created child element.
*/
private final class SubElementsCallback implements TransactionCallBack {
private final List mSubs;
public SubElementsCallback(final List subs) {
mSubs = subs;
}
@Override
public void performOn(final WriteableView item) throws TransactionFailure {
final ConfigBeanJMXSupport sptRoot = ConfigBeanJMXSupportRegistry.getInstance(com.sun.enterprise.config.serverbeans.Domain.class);
recursiveCreate(item, sptRoot, mSubs);
}
/**
* If the child is of a type matching an @Element that is a List, then get that list and add it to it.
*/
private void addToList(
final WriteableView parent,
final ConfigBeanProxy child) {
final Class extends ConfigBeanProxy> parentClass = parent.getProxyType();
final Class extends ConfigBeanProxy> childClass = Dom.unwrap(child).getProxyType();
final ConfigBeanJMXSupport parentSpt = ConfigBeanJMXSupportRegistry.getInstance(parentClass);
final ConfigBeanJMXSupport.ElementMethodInfo elementInfo = parentSpt.getElementMethodInfo(childClass);
final ConfigBean parentBean = (ConfigBean) Dom.unwrap(parent.getProxy(parentClass));
if (elementInfo != null && Collection.class.isAssignableFrom(elementInfo.method().getReturnType())) {
// get the Collection and add the child
final ConfigModel.Property modelProp = parentBean.model.findIgnoreCase(elementInfo.xmlName());
final List list = (List) parent.getter(modelProp, elementInfo.method().getGenericReturnType());
list.add(child);
} else if (elementInfo != null) {
final ConfigModel.Property modelProp = parentBean.model.findIgnoreCase(elementInfo.xmlName());
if (modelProp == null) {
throw new IllegalArgumentException("Can't find ConfigModel.Property for \"" + elementInfo.xmlName() + "\"");
}
parent.setter(modelProp, child, childClass);
}
}
private List recursiveCreate(
final WriteableView parent,
final ConfigBeanJMXSupport sptRoot,
final List subs) throws TransactionFailure {
final List newChildren = ListUtil.newList();
// create each sub-element, recursively
for (final CreateParams childParams : subs) {
final String type = childParams.type();
final Class extends ConfigBeanProxy> clazz = ConfigBeanJMXSupportRegistry.getConfigBeanProxyClassFor(sptRoot, type);
if (clazz == null) {
throw new IllegalArgumentException("@Configured interface for type " + type + " cannot be found");
}
final ConfigBeanJMXSupport spt = ConfigBeanJMXSupportRegistry.getInstance(clazz);
final ConfigBeanProxy childProxy = parent.allocateProxy(clazz);
Dom newBean = Dom.unwrap(childProxy);
newBean.addDefaultChildren();
addToList(parent, childProxy);
final ConfigBean child = (ConfigBean) Dom.unwrap(childProxy);
newChildren.add(child);
final WriteableView childW = WriteableView.class.cast(Proxy.getInvocationHandler(Proxy.class.cast(childProxy)));
final Map childAttrs = replaceNameWithKey(childParams.attrs(), spt);
setAttrs(childProxy, childAttrs);
if (!childParams.children().isEmpty()) {
final List more = recursiveCreate(childW, spt, childParams.children());
newChildren.addAll(more);
}
}
return newChildren;
}
}
public ObjectName removeChild(final String type) {
final ObjectName child = child(type);
if (child == null) {
logger.log(Level.SEVERE, AMXLoggerInfo.childNotfound, type);
return null;
}
return remove(child);
}
public ObjectName removeChild(final String type, final String name) {
final ObjectName child = child(type, name);
if (child == null) {
return null;
}
return remove(child);
}
private ObjectName remove(final ObjectName childObjectName) {
ObjectName removed = null;
try {
final ConfigBean childConfigBean = ConfigBeanRegistry.getInstance().getConfigBean(childObjectName);
try {
ConfigSupport.deleteChild(this.getConfigBean(), childConfigBean);
removed = childObjectName;
} catch (final TransactionFailure tf) {
throw new RuntimeException("Transaction failure deleting " + JMXUtil.toString(childObjectName), tf);
}
// NOTE: MBeans unregistered asynchronously by AMXConfigLoader
// enforce synchronous semantics to clients by waiting until this happens
// the listener is smart enough not to wait if it's already unregistered
final UnregistrationListener myListener = new UnregistrationListener(getMBeanServer(), childObjectName);
final long TIMEOUT_MILLIS = 10 * 1000;
final boolean unregisteredOK = myListener.waitForUnregister(TIMEOUT_MILLIS);
if (!unregisteredOK) {
throw new RuntimeException("Something went wrong unregistering MBean " + JMXUtil.toString(childObjectName));
}
} catch (final Exception e) {
throw new RuntimeException("Problem deleting " + childObjectName, e);
}
return removed;
}
private Object invokeDuckMethod(
final ConfigBeanJMXSupport.DuckTypedInfo info,
Object[] args)
throws MBeanException {
try {
if (!info.method().getDeclaringClass().isAssignableFrom(getConfigBeanProxy().getClass())) {
throw new IllegalArgumentException("invokeDuckMethod: " + getConfigBean().getProxyType() + " not asssignable to " + info.method().getDeclaringClass());
}
Object result = info.method().invoke(getConfigBeanProxy(), args);
result = translateResult(result);
return result;
} catch (final Exception e) {
throw new MBeanException(e);
}
}
private ObjectName getObjectName(final ConfigBeanProxy cbp) {
final Dom dom = Dom.unwrap(cbp);
if (dom instanceof ConfigBean) {
return ConfigBeanRegistry.getInstance().getObjectName((ConfigBean) dom);
}
// we can't return a Dom over the wire
return null;
}
/**
* Convert results that contain local ConfigBeanProxy into ObjectNames. Ignore other items, passing through unchanged.
*/
private Object translateResult(final Object result) {
// short-circuit the common case
if (result instanceof String) {
return result;
}
Object out = result;
// ConfigBean types must be mapped back to ObjectName; they can't go across the wire
if (result instanceof ConfigBeanProxy) {
out = getObjectName((ConfigBeanProxy) result);
} else if (result instanceof Collection) {
final Collection