/*
* JBoss, the OpenSource J2EE webOS
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.mx.modelmbean;
import java.util.Iterator;
import java.util.List;
import java.lang.reflect.Method;
import javax.management.AttributeChangeNotification;
import javax.management.AttributeChangeNotificationFilter;
import javax.management.AttributeList;
import javax.management.Attribute;
import javax.management.Descriptor;
import javax.management.InstanceNotFoundException;
import javax.management.JMException;
import javax.management.ListenerNotFoundException;
import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;
import javax.management.NotificationEmitter;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanException;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.RuntimeOperationsException;
import javax.management.modelmbean.ModelMBean;
import javax.management.modelmbean.ModelMBeanAttributeInfo;
import javax.management.modelmbean.ModelMBeanInfo;
import javax.management.modelmbean.ModelMBeanInfoSupport;
import javax.management.modelmbean.ModelMBeanNotificationInfo;
import javax.management.modelmbean.ModelMBeanOperationInfo;
import javax.management.modelmbean.DescriptorSupport;
import javax.management.modelmbean.InvalidTargetObjectTypeException;
import org.jboss.mx.server.AbstractMBeanInvoker;
import org.jboss.mx.server.InvocationContext;
import org.jboss.mx.interceptor.Interceptor;
import org.jboss.mx.interceptor.PersistenceInterceptor;
import org.jboss.mx.interceptor.ModelMBeanAttributeInterceptor;
import org.jboss.mx.interceptor.ModelMBeanInfoInterceptor;
import org.jboss.mx.persistence.NullPersistence;
import org.jboss.mx.persistence.PersistenceManager;
import org.jboss.mx.logging.Logger;
import org.jboss.mx.logging.SystemLogger;
/**
* An extension of the {@link org.jboss.mx.server.MBeanInvoker MBeanInvoker}
* that implements the base Model MBean functionality, essentially making the
* Model MBean just another invoker of managed resources.
*
* @see javax.management.modelmbean.ModelMBean
* @see org.jboss.mx.server.MBeanInvoker
*
* @author <a href="mailto:juha@jboss.org">Juha Lindfors</a>.
* @author Matt Munz
* @version $Revision: 1.10 $
*/
public abstract class ModelMBeanInvoker extends AbstractMBeanInvoker
implements ModelMBean, ModelMBeanConstants
{
Logger log = SystemLogger.getLogger(ModelMBeanInvoker.class.getName());
// Attributes ----------------------------------------------------
/**
* The resource type string of the managed resource, such as
* {@link ModelMBeanConstants#OBJECT_REF} or
* {@link XMBeanConstants#STANDARD_INTERFACE}. This type string can be
* used by the invoker to determine the behavior implemented by the
* invocation chain and how the managed resource is exposed to the client
* programs.
*/
protected String resourceType = null;
/**
* Persistence manager.
*/
protected PersistenceManager persistence = new NullPersistence();
/**
* Notification broadcaster for this Model MBean.
*/
protected NotificationBroadcasterSupport notifier = new NotificationBroadcasterSupport();
/**
* Notification sequence number for generic Model MBean notifications.
*/
protected long notifierSequence = 1;
/**
* Notification sequence number for attribute change notifications.
*/
protected long attrNotifierSequence = 1;
// Constructors --------------------------------------------------
/**
* Default constructor.
*/
public ModelMBeanInvoker()
{
super();
setDelayedRegistration(true);
}
/**
* Creates a Model MBean instance and initializes it with the given
* Model MBean metadata.
*
* @param info Model MBean metadata
*/
public ModelMBeanInvoker(ModelMBeanInfo info) throws MBeanException
{
this();
setModelMBeanInfo(info);
}
// ModelMBean implementation -------------------------------------
/**
* Sets the MBean metadata for this Model MBean instance.
*
* @param info Model MBean metadata
*/
public void setModelMBeanInfo(ModelMBeanInfo info) throws MBeanException, RuntimeOperationsException
{
if (info == null)
throw new IllegalArgumentException("MBeanInfo cannot be null.");
// need to type to an instance of MBeanInfo -- therefore the extra copy here
this.info = new ModelMBeanInfoSupport(info);
}
/**
* Sets the managed resource for this Model MBean instance. The resource
* type must be known to the Model MBean implementation (see
* {@link #isSupportedResourceType} for more information).
*
* @param ref reference to the managed resource
* @param resourceType resource type identification string
*/
public void setManagedResource(Object ref, String resourceType)
throws MBeanException, InstanceNotFoundException, InvalidTargetObjectTypeException
{
if (ref == null)
throw new IllegalArgumentException("Resource reference cannot be null.");
if (!isSupportedResourceType(ref, resourceType))
throw new InvalidTargetObjectTypeException("Unsupported resource type: " + resourceType);
setResource(ref);
this.resourceType = resourceType;
}
// ModelMBeanNotificationBroadcaster implementation --------------
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);
}
/**
* Sends a notification with a given string message. The notification
* type will be set as
* {@link ModelMBeanConstants#GENERIC_MODELMBEAN_NOTIFICATION GENERIC_MODELMBEAN_NOTIFICATION}.
*
* @param message notification message
*/
public void sendNotification(String message) throws MBeanException
{
Notification notif = new Notification(
GENERIC_MODELMBEAN_NOTIFICATION, // type
this, // source
++notifierSequence, // sequence number
message // message
);
sendNotification(notif);
}
/**
* Sends a notification.
*
* @param notification notification to send
*/
public void sendNotification(Notification notification) throws MBeanException
{
notifier.sendNotification(notification);
}
/**
* Sends an attribute change notification.
*
* @param notification attribute change notification to send
*/
public void sendAttributeChangeNotification(AttributeChangeNotification notification)
throws MBeanException
{
notifier.sendNotification(notification);
}
/**
* Sends an attribute change notification.
*
* @param oldValue attribute with the old value
* @param newValue attribute with the new value
*/
public void sendAttributeChangeNotification(Attribute oldValue, Attribute newValue)
throws MBeanException
{
if (!(oldValue.getName().equals(newValue.getName())))
throw 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, // source
++attrNotifierSequence, // seq. #
System.currentTimeMillis(), // time stamp
"" + attr + " changed from " + oldValue + " to " + newValue,
attr, type, // name & type
oldValue.getValue(),
newValue.getValue() // values
);
notifier.sendNotification(notif);
}
public MBeanNotificationInfo[] getNotificationInfo()
{
return info.getNotifications();
}
/**
* @deprecated use {@link #addNotificationListener} instead
*/
public void addAttributeChangeNotificationListener(
NotificationListener listener,
String attributeName,
Object handback) throws MBeanException
{
AttributeChangeNotificationFilter filter = new AttributeChangeNotificationFilter();
filter.enableAttribute(attributeName);
notifier.addNotificationListener(listener, filter, handback);
}
/**
* @deprecated use {@link #removeNotificationListener} instead
*/
public void removeAttributeChangeNotificationListener(
NotificationListener listener,
String attributeName) throws MBeanException, ListenerNotFoundException
{
notifier.removeNotificationListener(listener);
}
// PersistentMBean implementation --------------------------------
public void load() throws MBeanException, InstanceNotFoundException
{
if (info == null)
return;
persistence.load(this, info);
}
public void store() throws MBeanException, InstanceNotFoundException
{
persistence.store(info);
}
// MBeanRegistration implementation ------------------------------
/**
* The default implementation of <tt>preRegister</tt> invokes the
* {@link #configureInterceptorStack} method which sets up the interceptors
* for this Model MBean instance. Subclasses may override the
* <tt>configureInterceptorStack()</tt> method to implement their own
* interceptor stack configurations. See the JavaDoc for
* <tt>configureInterceptorStack()</tt> for more information. <p>
*
* After the interceptor configuration, this implementation invokes the
* {@link #load} method on this Model MBean instance. This will attempt
* to load a pre-existing management attribute state for this Model MBean
* instance. See the Javadoc for <tt>load()</tt> for more information.
*/
public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception
{
// initialize MBeanInfo first
// add the resource classname to the MBean info
ModelMBeanInfoSupport infoSupport = (ModelMBeanInfoSupport) info;
Descriptor mbeanDescriptor = infoSupport.getMBeanDescriptor();
mbeanDescriptor.setField(
ModelMBeanConstants.RESOURCE_CLASS,
getResource().getClass().getName());
// make sure we call super to ensure the MBeanInvoker is initialized
// before continuing
super.preRegister(server, name);
configureInterceptorStack((ModelMBeanInfo)info, server);
initPersistence(server);
load();
return invokePreRegister(server, name);
}
public void postRegister(Boolean registrationSuccessful)
{
invokePostRegister(registrationSuccessful);
}
public void preDeregister() throws Exception
{
invokePreDeregister();
}
public void postDeregister()
{
invokePostDeregister();
}
// Protected ---------------------------------------------------
/**
* initializes the persistence manager based on the info for this bean.
* If this is successful, loads the bean from the persistence store.
* @todo Should log all errors using log4j
*/
protected void initPersistence(MBeanServer server)
throws MBeanException, InstanceNotFoundException
{
Descriptor[] descriptors;
try { descriptors = ((ModelMBeanInfo)info).getDescriptors(MBEAN_DESCRIPTOR); }
catch(MBeanException cause)
{
cause.printStackTrace();
return;
}
if(descriptors == null) { return; }
Object persistMgrName = null;
for(int i = 0; ((i < descriptors.length) && (persistMgrName == null)); i++)
{
persistMgrName = descriptors[i].getFieldValue(PERSISTENCE_MANAGER);
}
if(persistMgrName == null) { return; } // use default -- null persistence
try { persistence = (PersistenceManager) server.instantiate((String) persistMgrName); }
catch(Exception cause)
{
log.error("Unable to instantiate the persistence manager.", cause);
}
}
protected void initOperationContexts(MBeanOperationInfo[] operations)
{
// make sure we invoke the super class initialization sequence first
super.initOperationContexts(operations);
for (int i = 0; i < operations.length; ++i)
{
OperationKey key = new OperationKey(operations[i]);
InvocationContext ctx = (InvocationContext)operationContextMap.get(key);
ctx.setDescriptor(((ModelMBeanOperationInfo)operations[i]).getDescriptor());
}
}
protected void initAttributeContexts(MBeanAttributeInfo[] attributes)
{
super.initAttributeContexts(attributes);
for (int i = 0; i < attributes.length; ++i)
{
InvocationContext ctx = (InvocationContext)attributeContextMap.get(attributes[i].getName());
ctx.setDescriptor(((ModelMBeanAttributeInfo)attributes[i]).getDescriptor());
}
}
protected void configureInterceptorStack(ModelMBeanInfo info, MBeanServer server)
throws MBeanException
{
Iterator it = attributeContextMap.keySet().iterator();
while (it.hasNext())
{
String key = (String)it.next();
InvocationContext ctx = (InvocationContext)attributeContextMap.get(key);
List list = (List)ctx.getInterceptors();
// at minimum all model mbeans have this interceptor
Interceptor interceptor = new ModelMBeanAttributeInterceptor();
//interceptor.setLogger(log);
list.add(interceptor);
}
List interceptors = getMBeanInfoCtx.getInterceptors();
interceptors.add(new ModelMBeanInfoInterceptor());
// @todo: review. copied from prior version
interceptors.add(new PersistenceInterceptor(
server,
this,
info.getDescriptors(ALL_DESCRIPTORS)));
// @todo: review. end copy.
getMBeanInfoCtx.setInterceptors(interceptors);
}
protected boolean isSupportedResourceType(Object resource, String resourceType)
{
if (resourceType.equals(OBJECT_REF))
return true;
return false;
}
}