package org.jboss.mx.modelmbean;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.beans.IntrospectionException;
import java.beans.PropertyEditorManager;
import java.beans.PropertyEditor;
import java.beans.PropertyDescriptor;
import java.beans.Introspector;
import java.beans.BeanInfo;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
import javax.management.Attribute;
import javax.management.AttributeChangeNotification;
import javax.management.AttributeChangeNotificationFilter;
import javax.management.Descriptor;
import javax.management.InstanceNotFoundException;
import javax.management.JMException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanException;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanServer;
import javax.management.Notification;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.RuntimeOperationsException;
import javax.management.RuntimeErrorException;
import javax.management.MBeanInfo;
import javax.management.modelmbean.InvalidTargetObjectTypeException;
import javax.management.modelmbean.ModelMBean;
import javax.management.modelmbean.ModelMBeanAttributeInfo;
import javax.management.modelmbean.ModelMBeanInfo;
import javax.management.modelmbean.ModelMBeanInfoSupport;
import javax.management.modelmbean.ModelMBeanOperationInfo;
import org.jboss.logging.Logger;
import org.jboss.mx.interceptor.AbstractInterceptor;
import org.jboss.mx.interceptor.Interceptor;
import org.jboss.mx.interceptor.ModelMBeanAttributeInterceptor;
import org.jboss.mx.interceptor.ModelMBeanInfoInterceptor;
import org.jboss.mx.interceptor.ModelMBeanOperationInterceptor;
import org.jboss.mx.interceptor.PersistenceInterceptor;
import org.jboss.mx.interceptor.ModelMBeanInterceptor;
import org.jboss.mx.interceptor.ObjectReferenceInterceptor;
import org.jboss.mx.interceptor.PersistenceInterceptor2;
import org.jboss.mx.interceptor.NullInterceptor;
import org.jboss.mx.persistence.NullPersistence;
import org.jboss.mx.persistence.PersistenceManager;
import org.jboss.mx.server.AbstractMBeanInvoker;
import org.jboss.mx.server.Invocation;
import org.jboss.mx.server.InvocationContext;
import org.jboss.mx.server.MBeanInvoker;
import org.jboss.mx.util.JBossNotificationBroadcasterSupport;
public abstract class ModelMBeanInvoker extends AbstractMBeanInvoker
implements ModelMBean, ModelMBeanConstants
{
Logger log = Logger.getLogger(ModelMBeanInvoker.class.getName());
protected String resourceType = null;
protected PersistenceManager persistence = new NullPersistence();
protected JBossNotificationBroadcasterSupport notifier = new JBossNotificationBroadcasterSupport();
protected long notifierSequence = 1;
protected long attrNotifierSequence = 1;
public ModelMBeanInvoker()
{
}
public ModelMBeanInvoker(ModelMBeanInfo info) throws MBeanException
{
setModelMBeanInfo(info);
}
public void setModelMBeanInfo(ModelMBeanInfo info)
throws MBeanException, RuntimeOperationsException
{
if (info == null)
throw new RuntimeOperationsException(new IllegalArgumentException("MBeanInfo cannot be null"));
this.info = new ModelMBeanInfoSupport(info);
ModelMBeanInfo minfo = info;
Descriptor mbeanDescriptor = null;
try
{
mbeanDescriptor = minfo.getDescriptor("",
ModelMBeanConstants.MBEAN_DESCRIPTOR);
}
catch (MBeanException e)
{
log.warn("Failed to obtain descriptor: "+ModelMBeanConstants.MBEAN_DESCRIPTOR, e);
return;
}
String type = (String) mbeanDescriptor.getFieldValue(
ModelMBeanConstants.MBEAN_INFO_INJECTION_TYPE);
if( type != null )
{
inject(ModelMBeanConstants.MBEAN_INFO_INJECTION_TYPE,
type, MBeanInfo.class, info);
}
}
public void setManagedResource(Object ref, String resourceType)
throws MBeanException, InstanceNotFoundException, InvalidTargetObjectTypeException
{
if (!isSupportedResourceType(ref, resourceType))
throw new InvalidTargetObjectTypeException("Unsupported resource type: " + resourceType);
setResource(ref);
this.resourceType = resourceType;
if (getServer() != null)
{
try
{
this.init(getServer(), resourceEntry.getObjectName());
}
catch(Exception e)
{
throw new MBeanException(e, "Failed to init from resource");
}
}
}
public void addNotificationListener(NotificationListener listener,
NotificationFilter filter,
Object handback)
{
notifier.addNotificationListener(listener, filter, handback);
}
public void removeNotificationListener(NotificationListener listener)
throws ListenerNotFoundException
{
notifier.removeNotificationListener(listener);
}
public void removeNotificationListener(NotificationListener listener,
NotificationFilter filter,
Object handback)
throws ListenerNotFoundException
{
notifier.removeNotificationListener(listener, filter, handback);
}
public void sendNotification(String ntfyText)
throws MBeanException, RuntimeOperationsException
{
if( ntfyText == null )
{
throw new RuntimeOperationsException(
new IllegalArgumentException("ntfyText cannot be null")
);
}
Notification notif = new Notification(
GENERIC_MODELMBEAN_NOTIFICATION, this, 1, ntfyText );
sendNotification(notif);
}
public void sendNotification(Notification ntfyObj)
throws MBeanException, RuntimeOperationsException
{
if( ntfyObj == null )
{
throw new RuntimeOperationsException(
new IllegalArgumentException("ntfyText cannot be null")
);
}
notifier.sendNotification(ntfyObj);
}
public void sendAttributeChangeNotification(AttributeChangeNotification notification)
throws MBeanException
{
if( notification == null )
{
throw new RuntimeOperationsException(
new IllegalArgumentException("notification cannot be null")
);
}
notifier.sendNotification(notification);
}
public void sendAttributeChangeNotification(Attribute oldValue, Attribute newValue)
throws MBeanException, RuntimeOperationsException
{
if( oldValue == null || newValue == null )
{
throw new RuntimeOperationsException(
new IllegalArgumentException("Attribute cannot be null")
);
}
if (!(oldValue.getName().equals(newValue.getName())))
{
throw new RuntimeOperationsException(
new IllegalArgumentException("Attribute name mismatch between oldvalue and newvalue")
);
}
String attr = oldValue.getName();
String type = ((ModelMBeanInfo) info).getAttribute(attr).getType();
AttributeChangeNotification notif = new AttributeChangeNotification(
this, 1, System.currentTimeMillis(), "" + attr + " changed from " + oldValue + " to " + newValue,
attr, type, oldValue.getValue(),
newValue.getValue() );
notifier.sendNotification(notif);
}
public MBeanNotificationInfo[] getNotificationInfo()
{
return info.getNotifications();
}
public void addAttributeChangeNotificationListener(
NotificationListener listener,
String attributeName,
Object handback) throws MBeanException
{
ModelMBeanInfo minfo = (ModelMBeanInfo) info;
AttributeChangeNotificationFilter filter = null;
if (attributeName != null)
{
ModelMBeanAttributeInfo ainfo = minfo.getAttribute(attributeName);
if( ainfo == null )
{
throw new RuntimeOperationsException(
new IllegalArgumentException("Attribute does not exist: "+attributeName));
}
filter = new AttributeChangeNotificationFilter();
filter.enableAttribute(attributeName);
}
else
{
filter = new AttributeChangeNotificationFilter();
MBeanAttributeInfo[] allAttributes = minfo.getAttributes();
for (int i = 0; i < allAttributes.length; ++i)
filter.enableAttribute(allAttributes[i].getName());
}
notifier.addNotificationListener(listener, filter, handback);
}
public void removeAttributeChangeNotificationListener(
NotificationListener listener,
String attributeName) throws MBeanException, ListenerNotFoundException
{
if( attributeName != null )
{
ModelMBeanInfo minfo = (ModelMBeanInfo) info;
ModelMBeanAttributeInfo ainfo = minfo.getAttribute(attributeName);
if( ainfo == null )
{
throw new RuntimeOperationsException(
new IllegalArgumentException("Attribute does not exist: "+attributeName));
}
}
notifier.removeNotificationListener(listener);
}
public void load() throws MBeanException, InstanceNotFoundException
{
if (info == null)
return;
persistence.load(this, info);
}
public void store() throws MBeanException, InstanceNotFoundException
{
persistence.store(info);
}
public ObjectName invokePreRegister(MBeanServer server, ObjectName name)
throws Exception
{
if (info == null)
{
throw new RuntimeErrorException(
new Error("MBeanInfo has not been set."));
}
final ModelMBeanInfo minfo = (ModelMBeanInfo) info;
Descriptor mbeanDescriptor = minfo.getMBeanDescriptor();
getMBeanInfoCtx = new InvocationContext();
getMBeanInfoCtx.setInvoker(this);
getMBeanInfoCtx.setDescriptor(mbeanDescriptor);
getMBeanInfoCtx.setDispatcher(new AbstractInterceptor("MBeanInfo Dispatcher")
{
public Object invoke(Invocation invocation) throws Throwable
{
return minfo;
}
});
String[] signature = {};
OperationKey opKey = new OperationKey("getMBeanInfo", signature);
operationContextMap.put(opKey, getMBeanInfoCtx);
signature = new String[]{"java.lang.Object", "java.lang.String"};
opKey = new OperationKey("setManagedResource", signature);
InvocationContext ctx = new InvocationContext();
ctx.setInvoker(this);
ctx.setDispatcher(new AbstractInterceptor("SetMangedResource Dispatcher")
{
public Object invoke(Invocation invocation) throws Throwable
{
Object[] args = invocation.getArgs();
setManagedResource(args[0], (String) args[1]);
return null;
}
});
operationContextMap.put(opKey, ctx);
if (getResource() == null )
{
return name;
}
else
{
init(server, name);
}
return super.invokePreRegister(server, name);
}
protected void init(MBeanServer server, ObjectName name)
throws Exception
{
ModelMBeanInfo minfo = (ModelMBeanInfo) info;
configureInterceptorStack(minfo, server, name);
initDispatchers();
Object resource = getResource();
if (resource != null)
{
Descriptor mbeanDescriptor = minfo.getMBeanDescriptor();
String resClassName = getResource().getClass().getName();
mbeanDescriptor.setField(ModelMBeanConstants.RESOURCE_CLASS, resClassName);
minfo.setMBeanDescriptor(mbeanDescriptor);
}
setValuesFromMBeanInfo();
initPersistence(server, name);
load();
}
protected void initPersistence(MBeanServer server, ObjectName name)
throws MBeanException, InstanceNotFoundException
{
Descriptor[] descriptors;
ModelMBeanInfo minfo = (ModelMBeanInfo) getMetaData();
try
{
descriptors = minfo.getDescriptors(MBEAN_DESCRIPTOR);
}
catch (MBeanException e)
{
log.error("Failed to obtain MBEAN_DESCRIPTORs", e);
return;
}
if (descriptors == null)
{
return;
}
String persistMgrName = null;
for (int i = 0; ((i < descriptors.length) && (persistMgrName == null)); i++)
{
persistMgrName = (String) descriptors[i].getFieldValue(PERSISTENCE_MANAGER);
}
if (persistMgrName == null)
{
log.trace("No " + PERSISTENCE_MANAGER
+ " descriptor found, null persistence will be used");
return;
}
try
{
persistence = (PersistenceManager) server.instantiate(persistMgrName);
log.debug("Loaded persistence mgr: " + persistMgrName);
Descriptor descriptor = minfo.getMBeanDescriptor();
descriptor.setField(ModelMBeanConstants.OBJECT_NAME, name);
minfo.setMBeanDescriptor(descriptor);
}
catch (Exception cause)
{
log.error("Unable to instantiate the persistence manager:"
+ persistMgrName, cause);
}
}
protected void initOperationContexts(MBeanOperationInfo[] operations)
{
super.initOperationContexts(operations);
for (int i = 0; i < operations.length; ++i)
{
OperationKey key = new OperationKey(operations[i]);
InvocationContext ctx = (InvocationContext) operationContextMap.get(key);
ModelMBeanOperationInfo info = (ModelMBeanOperationInfo) operations[i];
ctx.setDescriptor(info.getDescriptor());
}
}
protected void initAttributeContexts(MBeanAttributeInfo[] attributes)
{
super.initAttributeContexts(attributes);
for (int i = 0; i < attributes.length; ++i)
{
ModelMBeanAttributeInfo info = (ModelMBeanAttributeInfo) attributes[i];
String name = info.getName();
InvocationContext ctx = (InvocationContext) attributeContextMap.get(name);
ctx.setDescriptor(info.getDescriptor());
ctx.setReadable(info.isReadable());
ctx.setWritable(info.isWritable());
}
}
protected void configureInterceptorStack(ModelMBeanInfo info,
MBeanServer server, ObjectName name)
throws Exception
{
List defaultInterceptors = getInterceptors(getMBeanInfoCtx.getDescriptor());
List interceptors = null;
if (defaultInterceptors != null)
interceptors = new ArrayList(defaultInterceptors);
if (interceptors == null)
{
interceptors = getMBeanInfoCtx.getInterceptors();
}
String mbeanName = name != null ? name.toString() : info.getClassName();
interceptors.add(new ModelMBeanInfoInterceptor(mbeanName));
getMBeanInfoCtx.setInterceptors(interceptors);
for (Iterator it = attributeContextMap.entrySet().iterator(); it.hasNext();)
{
Map.Entry entry = (Map.Entry) it.next();
InvocationContext ctx = (InvocationContext) entry.getValue();
List list = getInterceptors(ctx.getDescriptor());
if (list == null)
{
if (defaultInterceptors != null)
list = new ArrayList(defaultInterceptors);
else
list = new ArrayList();
}
list.add(new PersistenceInterceptor());
list.add(new ModelMBeanAttributeInterceptor());
ctx.setInterceptors(list);
}
for (Iterator it = operationContextMap.entrySet().iterator(); it.hasNext();)
{
Map.Entry entry = (Map.Entry) it.next();
InvocationContext ctx = (InvocationContext) entry.getValue();
List list = getInterceptors(ctx.getDescriptor());
if (list == null && defaultInterceptors != null)
list = new ArrayList(defaultInterceptors);
if (dynamicResource)
{
if (list == null)
list = new ArrayList();
list.add(new ModelMBeanOperationInterceptor());
}
if (list != null)
{
list.add(new NullInterceptor());
ctx.setInterceptors(list);
}
}
}
protected List getInterceptors(Descriptor d) throws Exception
{
if (d == null)
return null;
Descriptor[] interceptorDescriptors = (Descriptor[]) d.getFieldValue(INTERCEPTORS);
if (interceptorDescriptors == null)
return null;
ArrayList interceptors = new ArrayList();
ClassLoader loader = Thread.currentThread().getContextClassLoader();
for (int i = 0; i < interceptorDescriptors.length; i++)
{
Descriptor desc = interceptorDescriptors[i];
String code = (String) desc.getFieldValue("code");
if (code.equals(ModelMBeanInterceptor.class.getName()) ||
code.equals(ObjectReferenceInterceptor.class.getName()) ||
code.equals(PersistenceInterceptor2.class.getName()))
{
log.debug("Ignoring obsolete legacy interceptor: " + code);
continue;
}
Class interceptorClass = loader.loadClass(code);
Interceptor interceptor = null;
Class[] ctorSig = {MBeanInvoker.class};
try
{
Constructor ctor = interceptorClass.getConstructor(ctorSig);
Object[] ctorArgs = {this};
interceptor = (Interceptor) ctor.newInstance(ctorArgs);
}
catch (Throwable t)
{
log.debug("Failed to invoke ctor(MBeanInvoker) for: "
+ interceptorClass, t);
interceptor = (Interceptor) interceptorClass.newInstance();
}
interceptors.add(interceptor);
String[] names = desc.getFieldNames();
HashMap propertyMap = new HashMap();
if (names.length > 1)
{
BeanInfo beanInfo = Introspector.getBeanInfo(interceptorClass);
PropertyDescriptor[] props = beanInfo.getPropertyDescriptors();
for (int p = 0; p < props.length; p++)
{
String fieldName = props[p].getName();
propertyMap.put(fieldName, props[p]);
}
for (int n = 0; n < names.length; n++)
{
String name = names[n];
if (name.equals("code"))
continue;
String text = (String) desc.getFieldValue(name);
PropertyDescriptor pd = (PropertyDescriptor) propertyMap.get(name);
if (pd == null)
throw new IntrospectionException("No PropertyDescriptor for attribute:" + name);
Method setter = pd.getWriteMethod();
if (setter != null)
{
Class ptype = pd.getPropertyType();
PropertyEditor editor = PropertyEditorManager.findEditor(ptype);
if (editor == null)
throw new IntrospectionException("Cannot convert string to interceptor attribute:" + name);
editor.setAsText(text);
Object args[] = {editor.getValue()};
setter.invoke(interceptor, args);
}
}
}
}
if (interceptors.size() == 0)
interceptors = null;
return interceptors;
}
protected void setValuesFromMBeanInfo() throws JMException
{
for (Iterator it = attributeContextMap.entrySet().iterator(); it.hasNext();)
{
Map.Entry entry = (Map.Entry) it.next();
String key = (String) entry.getKey();
InvocationContext ctx = (InvocationContext) entry.getValue();
Object value = ctx.getDescriptor().getFieldValue(XMBeanConstants.CACHED_VALUE);
if (value != null)
{
setAttribute(new Attribute(key, value));
} }
}
protected boolean isSupportedResourceType(Object resource, String resourceType)
{
if (resourceType.equalsIgnoreCase(OBJECT_REF))
return true;
return false;
}
protected void override(Invocation invocation) throws MBeanException
{
if (dynamicResource && info != null)
{
Descriptor current = invocation.getDescriptor();
if (current != null)
{
ModelMBeanInfo mminfo = (ModelMBeanInfo) info;
Descriptor descriptor = mminfo.getDescriptor((String) current.getFieldValue(NAME), (String) current.getFieldValue(DESCRIPTOR_TYPE));
if (descriptor != null)
invocation.setDescriptor(descriptor);
}
}
}
}