This commit is contained in:
2024-11-30 19:03:49 +08:00
commit 1e6763c160
3806 changed files with 737676 additions and 0 deletions

View File

@@ -0,0 +1,164 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.modeler;
import javax.management.MBeanAttributeInfo;
/**
* <p>Internal configuration information for an <code>Attribute</code>
* descriptor.</p>
*
* @author Craig R. McClanahan
*/
public class AttributeInfo extends FeatureInfo {
static final long serialVersionUID = -2511626862303972143L;
// ----------------------------------------------------- Instance Variables
protected String displayName = null;
// Information about the method to use
protected String getMethod = null;
protected String setMethod = null;
protected boolean readable = true;
protected boolean writeable = true;
protected boolean is = false;
// ------------------------------------------------------------- Properties
/**
* @return the display name of this attribute.
*/
public String getDisplayName() {
return this.displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
/**
* @return the name of the property getter method, if non-standard.
*/
public String getGetMethod() {
if(getMethod == null)
getMethod = getMethodName(getName(), true, isIs());
return this.getMethod;
}
public void setGetMethod(String getMethod) {
this.getMethod = getMethod;
}
/**
* Is this a boolean attribute with an "is" getter?
* @return <code>true</code> if this is a boolean attribute
* with an "is" getter
*/
public boolean isIs() {
return this.is;
}
public void setIs(boolean is) {
this.is = is;
}
/**
* Is this attribute readable by management applications?
* @return <code>true</code> if readable
*/
public boolean isReadable() {
return this.readable;
}
public void setReadable(boolean readable) {
this.readable = readable;
}
/**
* @return the name of the property setter method, if non-standard.
*/
public String getSetMethod() {
if( setMethod == null )
setMethod = getMethodName(getName(), false, false);
return this.setMethod;
}
public void setSetMethod(String setMethod) {
this.setMethod = setMethod;
}
/**
* Is this attribute writable by management applications?
* @return <code>true</code> if writable
*/
public boolean isWriteable() {
return this.writeable;
}
public void setWriteable(boolean writeable) {
this.writeable = writeable;
}
// --------------------------------------------------------- Public Methods
/**
* Create and return a <code>ModelMBeanAttributeInfo</code> object that
* corresponds to the attribute described by this instance.
* @return the attribute info
*/
MBeanAttributeInfo createAttributeInfo() {
// Return our cached information (if any)
if (info == null) {
info = new MBeanAttributeInfo(getName(), getType(), getDescription(),
isReadable(), isWriteable(), false);
}
return (MBeanAttributeInfo)info;
}
// -------------------------------------------------------- Private Methods
/**
* Create and return the name of a default property getter or setter
* method, according to the specified values.
*
* @param name Name of the property itself
* @param getter Do we want a get method (versus a set method)?
* @param is If returning a getter, do we want the "is" form?
* @return the method name
*/
private String getMethodName(String name, boolean getter, boolean is) {
StringBuilder sb = new StringBuilder();
if (getter) {
if (is)
sb.append("is");
else
sb.append("get");
} else
sb.append("set");
sb.append(Character.toUpperCase(name.charAt(0)));
sb.append(name.substring(1));
return sb.toString();
}
}

View File

@@ -0,0 +1,160 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.modeler;
import java.util.HashSet;
import javax.management.AttributeChangeNotification;
import javax.management.Notification;
import javax.management.NotificationFilter;
/**
* <p>Implementation of <code>NotificationFilter</code> for attribute change
* notifications. This class is used by <code>BaseModelMBean</code> to
* construct attribute change notification event filters when a filter is not
* supplied by the application.</p>
*
* @author Craig R. McClanahan
*/
public class BaseAttributeFilter implements NotificationFilter {
private static final long serialVersionUID = 1L;
// ----------------------------------------------------------- Constructors
/**
* Construct a new filter that accepts only the specified attribute
* name.
*
* @param name Name of the attribute to be accepted by this filter, or
* <code>null</code> to accept all attribute names
*/
public BaseAttributeFilter(String name) {
super();
if (name != null)
addAttribute(name);
}
// ----------------------------------------------------- Instance Variables
/**
* The set of attribute names that are accepted by this filter. If this
* list is empty, all attribute names are accepted.
*/
private HashSet<String> names = new HashSet<>();
// --------------------------------------------------------- Public Methods
/**
* Add a new attribute name to the set of names accepted by this filter.
*
* @param name Name of the attribute to be accepted
*/
public void addAttribute(String name) {
synchronized (names) {
names.add(name);
}
}
/**
* Clear all accepted names from this filter, so that it will accept
* all attribute names.
*/
public void clear() {
synchronized (names) {
names.clear();
}
}
/**
* Return the set of names that are accepted by this filter. If this
* filter accepts all attribute names, a zero length array will be
* returned.
* @return the array of names
*/
public String[] getNames() {
synchronized (names) {
return names.toArray(new String[names.size()]);
}
}
/**
* <p>Test whether notification enabled for this event.
* Return true if:</p>
* <ul>
* <li>This is an attribute change notification</li>
* <li>Either the set of accepted names is empty (implying that all
* attribute names are of interest) or the set of accepted names
* includes the name of the attribute in this notification</li>
* </ul>
*/
@Override
public boolean isNotificationEnabled(Notification notification) {
if (notification == null)
return false;
if (!(notification instanceof AttributeChangeNotification))
return false;
AttributeChangeNotification acn =
(AttributeChangeNotification) notification;
if (!AttributeChangeNotification.ATTRIBUTE_CHANGE.equals(acn.getType()))
return false;
synchronized (names) {
if (names.size() < 1)
return true;
else
return names.contains(acn.getAttributeName());
}
}
/**
* Remove an attribute name from the set of names accepted by this
* filter.
*
* @param name Name of the attribute to be removed
*/
public void removeAttribute(String name) {
synchronized (names) {
names.remove(name);
}
}
}

View File

@@ -0,0 +1,915 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.modeler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Iterator;
import javax.management.Attribute;
import javax.management.AttributeChangeNotification;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.DynamicMBean;
import javax.management.InstanceNotFoundException;
import javax.management.InvalidAttributeValueException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanRegistration;
import javax.management.MBeanServer;
import javax.management.Notification;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import javax.management.RuntimeErrorException;
import javax.management.RuntimeOperationsException;
import javax.management.modelmbean.InvalidTargetObjectTypeException;
import javax.management.modelmbean.ModelMBeanNotificationBroadcaster;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
/*
* Changes from commons.modeler:
*
* - use DynamicMBean
* - remove methods not used in tomcat and redundant/not very generic
* - must be created from the ManagedBean - I don't think there were any direct
* uses, but now it is required.
* - some of the gratuitous flexibility removed - instead this is more predictive and
* strict with the use cases.
* - all Method and metadata is stored in ManagedBean. BaseModelBMean and ManagedBean act
* like Object and Class.
* - setModelMBean is no longer called on resources ( not used in tomcat )
* - no caching of Methods for now - operations and setters are not called repeatedly in most
* management use cases. Getters shouldn't be called very frequently either - and even if they
* are, the overhead of getting the method should be small compared with other JMX costs ( RMI, etc ).
* We can add getter cache if needed.
* - removed unused constructor, fields
*
* TODO:
* - clean up catalina.mbeans, stop using weird inheritance
*/
/**
* <p>Basic implementation of the <code>DynamicMBean</code> interface, which
* supports the minimal requirements of the interface contract.</p>
*
* <p>This can be used directly to wrap an existing java bean, or inside
* an mlet or anywhere an MBean would be used.
*
* Limitations:
* <ul>
* <li>Only managed resources of type <code>objectReference</code> are
* supported.</li>
* <li>Caching of attribute values and operation results is not supported.
* All calls to <code>invoke()</code> are immediately executed.</li>
* <li>Persistence of MBean attributes and operations is not supported.</li>
* <li>All classes referenced as attribute types, operation parameters, or
* operation return values must be one of the following:
* <ul>
* <li>One of the Java primitive types (boolean, byte, char, double,
* float, integer, long, short). Corresponding value will be wrapped
* in the appropriate wrapper class automatically.</li>
* <li>Operations that return no value should declare a return type of
* <code>void</code>.</li>
* </ul>
* <li>Attribute caching is not supported</li>
* </ul>
*
* @author Craig R. McClanahan
* @author Costin Manolache
*/
public class BaseModelMBean implements DynamicMBean, MBeanRegistration,
ModelMBeanNotificationBroadcaster {
private static final Log log = LogFactory.getLog(BaseModelMBean.class);
// ----------------------------------------------------- Instance Variables
protected ObjectName oname=null;
/**
* Notification broadcaster for attribute changes.
*/
protected BaseNotificationBroadcaster attributeBroadcaster = null;
/**
* Notification broadcaster for general notifications.
*/
protected BaseNotificationBroadcaster generalBroadcaster = null;
/** Metadata for the mbean instance.
*/
protected ManagedBean managedBean = null;
/**
* The managed resource this MBean is associated with (if any).
*/
protected Object resource = null;
// --------------------------------------------------- DynamicMBean Methods
// TODO: move to ManagedBean
static final Object[] NO_ARGS_PARAM = new Object[0];
protected String resourceType = null;
// key: operation val: invoke method
//private Hashtable invokeAttMap=new Hashtable();
/**
* Obtain and return the value of a specific attribute of this MBean.
*
* @param name Name of the requested attribute
*
* @exception AttributeNotFoundException if this attribute is not
* supported by this MBean
* @exception MBeanException if the initializer of an object
* throws an exception
* @exception ReflectionException if a Java reflection exception
* occurs when invoking the getter
*/
@Override
public Object getAttribute(String name)
throws AttributeNotFoundException, MBeanException,
ReflectionException {
// Validate the input parameters
if (name == null)
throw new RuntimeOperationsException
(new IllegalArgumentException("Attribute name is null"),
"Attribute name is null");
if( (resource instanceof DynamicMBean) &&
! ( resource instanceof BaseModelMBean )) {
return ((DynamicMBean)resource).getAttribute(name);
}
Method m=managedBean.getGetter(name, this, resource);
Object result = null;
try {
Class<?> declaring = m.getDeclaringClass();
// workaround for catalina weird mbeans - the declaring class is BaseModelMBean.
// but this is the catalina class.
if( declaring.isAssignableFrom(this.getClass()) ) {
result = m.invoke(this, NO_ARGS_PARAM );
} else {
result = m.invoke(resource, NO_ARGS_PARAM );
}
} catch (InvocationTargetException e) {
Throwable t = e.getTargetException();
if (t == null)
t = e;
if (t instanceof RuntimeException)
throw new RuntimeOperationsException
((RuntimeException) t, "Exception invoking method " + name);
else if (t instanceof Error)
throw new RuntimeErrorException
((Error) t, "Error invoking method " + name);
else
throw new MBeanException
(e, "Exception invoking method " + name);
} catch (Exception e) {
throw new MBeanException
(e, "Exception invoking method " + name);
}
// Return the results of this method invocation
// FIXME - should we validate the return type?
return result;
}
/**
* Obtain and return the values of several attributes of this MBean.
*
* @param names Names of the requested attributes
*/
@Override
public AttributeList getAttributes(String names[]) {
// Validate the input parameters
if (names == null)
throw new RuntimeOperationsException
(new IllegalArgumentException("Attribute names list is null"),
"Attribute names list is null");
// Prepare our response, eating all exceptions
AttributeList response = new AttributeList();
for (int i = 0; i < names.length; i++) {
try {
response.add(new Attribute(names[i],getAttribute(names[i])));
} catch (Exception e) {
// Not having a particular attribute in the response
// is the indication of a getter problem
}
}
return response;
}
public void setManagedBean(ManagedBean managedBean) {
this.managedBean = managedBean;
}
/**
* Return the <code>MBeanInfo</code> object for this MBean.
*/
@Override
public MBeanInfo getMBeanInfo() {
return managedBean.getMBeanInfo();
}
/**
* Invoke a particular method on this MBean, and return any returned
* value.
*
* <p><strong>IMPLEMENTATION NOTE</strong> - This implementation will
* attempt to invoke this method on the MBean itself, or (if not
* available) on the managed resource object associated with this
* MBean.</p>
*
* @param name Name of the operation to be invoked
* @param params Array containing the method parameters of this operation
* @param signature Array containing the class names representing
* the signature of this operation
*
* @exception MBeanException if the initializer of an object
* throws an exception
* @exception ReflectionException if a Java reflection exception
* occurs when invoking a method
*/
@Override
public Object invoke(String name, Object params[], String signature[])
throws MBeanException, ReflectionException
{
if( (resource instanceof DynamicMBean) &&
! ( resource instanceof BaseModelMBean )) {
return ((DynamicMBean)resource).invoke(name, params, signature);
}
// Validate the input parameters
if (name == null)
throw new RuntimeOperationsException
(new IllegalArgumentException("Method name is null"),
"Method name is null");
if( log.isDebugEnabled()) log.debug("Invoke " + name);
Method method= managedBean.getInvoke(name, params, signature, this, resource);
// Invoke the selected method on the appropriate object
Object result = null;
try {
if( method.getDeclaringClass().isAssignableFrom( this.getClass()) ) {
result = method.invoke(this, params );
} else {
result = method.invoke(resource, params);
}
} catch (InvocationTargetException e) {
Throwable t = e.getTargetException();
log.error("Exception invoking method " + name , t );
if (t == null)
t = e;
if (t instanceof RuntimeException)
throw new RuntimeOperationsException
((RuntimeException) t, "Exception invoking method " + name);
else if (t instanceof Error)
throw new RuntimeErrorException
((Error) t, "Error invoking method " + name);
else
throw new MBeanException
((Exception)t, "Exception invoking method " + name);
} catch (Exception e) {
log.error("Exception invoking method " + name , e );
throw new MBeanException
(e, "Exception invoking method " + name);
}
// Return the results of this method invocation
// FIXME - should we validate the return type?
return result;
}
static Class<?> getAttributeClass(String signature)
throws ReflectionException
{
if (signature.equals(Boolean.TYPE.getName()))
return Boolean.TYPE;
else if (signature.equals(Byte.TYPE.getName()))
return Byte.TYPE;
else if (signature.equals(Character.TYPE.getName()))
return Character.TYPE;
else if (signature.equals(Double.TYPE.getName()))
return Double.TYPE;
else if (signature.equals(Float.TYPE.getName()))
return Float.TYPE;
else if (signature.equals(Integer.TYPE.getName()))
return Integer.TYPE;
else if (signature.equals(Long.TYPE.getName()))
return Long.TYPE;
else if (signature.equals(Short.TYPE.getName()))
return Short.TYPE;
else {
try {
ClassLoader cl=Thread.currentThread().getContextClassLoader();
if( cl!=null )
return cl.loadClass(signature);
} catch( ClassNotFoundException e ) {
}
try {
return Class.forName(signature);
} catch (ClassNotFoundException e) {
throw new ReflectionException
(e, "Cannot find Class for " + signature);
}
}
}
/**
* Set the value of a specific attribute of this MBean.
*
* @param attribute The identification of the attribute to be set
* and the new value
*
* @exception AttributeNotFoundException if this attribute is not
* supported by this MBean
* @exception MBeanException if the initializer of an object
* throws an exception
* @exception ReflectionException if a Java reflection exception
* occurs when invoking the getter
*/
@Override
public void setAttribute(Attribute attribute)
throws AttributeNotFoundException, MBeanException,
ReflectionException
{
if( log.isDebugEnabled() )
log.debug("Setting attribute " + this + " " + attribute );
if( (resource instanceof DynamicMBean) &&
! ( resource instanceof BaseModelMBean )) {
try {
((DynamicMBean)resource).setAttribute(attribute);
} catch (InvalidAttributeValueException e) {
throw new MBeanException(e);
}
return;
}
// Validate the input parameters
if (attribute == null)
throw new RuntimeOperationsException
(new IllegalArgumentException("Attribute is null"),
"Attribute is null");
String name = attribute.getName();
Object value = attribute.getValue();
if (name == null)
throw new RuntimeOperationsException
(new IllegalArgumentException("Attribute name is null"),
"Attribute name is null");
Object oldValue=null;
//if( getAttMap.get(name) != null )
// oldValue=getAttribute( name );
Method m=managedBean.getSetter(name,this,resource);
try {
if( m.getDeclaringClass().isAssignableFrom( this.getClass()) ) {
m.invoke(this, new Object[] { value });
} else {
m.invoke(resource, new Object[] { value });
}
} catch (InvocationTargetException e) {
Throwable t = e.getTargetException();
if (t == null)
t = e;
if (t instanceof RuntimeException)
throw new RuntimeOperationsException
((RuntimeException) t, "Exception invoking method " + name);
else if (t instanceof Error)
throw new RuntimeErrorException
((Error) t, "Error invoking method " + name);
else
throw new MBeanException
(e, "Exception invoking method " + name);
} catch (Exception e) {
log.error("Exception invoking method " + name , e );
throw new MBeanException
(e, "Exception invoking method " + name);
}
try {
sendAttributeChangeNotification(new Attribute( name, oldValue),
attribute);
} catch(Exception ex) {
log.error("Error sending notification " + name, ex);
}
//attributes.put( name, value );
// if( source != null ) {
// // this mbean is associated with a source - maybe we want to persist
// source.updateField(oname, name, value);
// }
}
@Override
public String toString() {
if( resource==null )
return "BaseModelMbean[" + resourceType + "]";
return resource.toString();
}
/**
* Set the values of several attributes of this MBean.
*
* @param attributes THe names and values to be set
*
* @return The list of attributes that were set and their new values
*/
@Override
public AttributeList setAttributes(AttributeList attributes) {
AttributeList response = new AttributeList();
// Validate the input parameters
if (attributes == null)
return response;
// Prepare and return our response, eating all exceptions
String names[] = new String[attributes.size()];
int n = 0;
Iterator<?> items = attributes.iterator();
while (items.hasNext()) {
Attribute item = (Attribute) items.next();
names[n++] = item.getName();
try {
setAttribute(item);
} catch (Exception e) {
// Ignore all exceptions
}
}
return getAttributes(names);
}
// ----------------------------------------------------- ModelMBean Methods
/**
* Get the instance handle of the object against which we execute
* all methods in this ModelMBean management interface.
*
* @return the backend managed object
* @exception InstanceNotFoundException if the managed resource object
* cannot be found
* @exception InvalidTargetObjectTypeException if the managed resource
* object is of the wrong type
* @exception MBeanException if the initializer of the object throws
* an exception
* @exception RuntimeOperationsException if the managed resource or the
* resource type is <code>null</code> or invalid
*/
public Object getManagedResource()
throws InstanceNotFoundException, InvalidTargetObjectTypeException,
MBeanException, RuntimeOperationsException {
if (resource == null)
throw new RuntimeOperationsException
(new IllegalArgumentException("Managed resource is null"),
"Managed resource is null");
return resource;
}
/**
* Set the instance handle of the object against which we will execute
* all methods in this ModelMBean management interface.
*
* The caller can provide the mbean instance or the object name to
* the resource, if needed.
*
* @param resource The resource object to be managed
* @param type The type of reference for the managed resource
* ("ObjectReference", "Handle", "IOR", "EJBHandle", or
* "RMIReference")
*
* @exception InstanceNotFoundException if the managed resource object
* cannot be found
* @exception MBeanException if the initializer of the object throws
* an exception
* @exception RuntimeOperationsException if the managed resource or the
* resource type is <code>null</code> or invalid
*/
public void setManagedResource(Object resource, String type)
throws InstanceNotFoundException,
MBeanException, RuntimeOperationsException
{
if (resource == null)
throw new RuntimeOperationsException
(new IllegalArgumentException("Managed resource is null"),
"Managed resource is null");
// if (!"objectreference".equalsIgnoreCase(type))
// throw new InvalidTargetObjectTypeException(type);
this.resource = resource;
this.resourceType = resource.getClass().getName();
// // Make the resource aware of the model mbean.
// try {
// Method m=resource.getClass().getMethod("setModelMBean",
// new Class[] {ModelMBean.class});
// if( m!= null ) {
// m.invoke(resource, new Object[] {this});
// }
// } catch( NoSuchMethodException t ) {
// // ignore
// } catch( Throwable t ) {
// log.error( "Can't set model mbean ", t );
// }
}
// ------------------------------ ModelMBeanNotificationBroadcaster Methods
/**
* Add an attribute change notification event listener to this MBean.
*
* @param listener Listener that will receive event notifications
* @param name Name of the attribute of interest, or <code>null</code>
* to indicate interest in all attributes
* @param handback Handback object to be sent along with event
* notifications
*
* @exception IllegalArgumentException if the listener parameter is null
*/
@Override
public void addAttributeChangeNotificationListener
(NotificationListener listener, String name, Object handback)
throws IllegalArgumentException {
if (listener == null)
throw new IllegalArgumentException("Listener is null");
if (attributeBroadcaster == null)
attributeBroadcaster = new BaseNotificationBroadcaster();
if( log.isDebugEnabled() )
log.debug("addAttributeNotificationListener " + listener);
BaseAttributeFilter filter = new BaseAttributeFilter(name);
attributeBroadcaster.addNotificationListener
(listener, filter, handback);
}
/**
* Remove an attribute change notification event listener from
* this MBean.
*
* @param listener The listener to be removed
* @param name The attribute name for which no more events are required
*
*
* @exception ListenerNotFoundException if this listener is not
* registered in the MBean
*/
@Override
public void removeAttributeChangeNotificationListener
(NotificationListener listener, String name)
throws ListenerNotFoundException {
if (listener == null)
throw new IllegalArgumentException("Listener is null");
// FIXME - currently this removes *all* notifications for this listener
if (attributeBroadcaster != null) {
attributeBroadcaster.removeNotificationListener(listener);
}
}
/**
* Send an <code>AttributeChangeNotification</code> to all registered
* listeners.
*
* @param notification The <code>AttributeChangeNotification</code>
* that will be passed
*
* @exception MBeanException if an object initializer throws an
* exception
* @exception RuntimeOperationsException wraps IllegalArgumentException
* when the specified notification is <code>null</code> or invalid
*/
@Override
public void sendAttributeChangeNotification
(AttributeChangeNotification notification)
throws MBeanException, RuntimeOperationsException {
if (notification == null)
throw new RuntimeOperationsException
(new IllegalArgumentException("Notification is null"),
"Notification is null");
if (attributeBroadcaster == null)
return; // This means there are no registered listeners
if( log.isDebugEnabled() )
log.debug( "AttributeChangeNotification " + notification );
attributeBroadcaster.sendNotification(notification);
}
/**
* Send an <code>AttributeChangeNotification</code> to all registered
* listeners.
*
* @param oldValue The original value of the <code>Attribute</code>
* @param newValue The new value of the <code>Attribute</code>
*
* @exception MBeanException if an object initializer throws an
* exception
* @exception RuntimeOperationsException wraps IllegalArgumentException
* when the specified notification is <code>null</code> or invalid
*/
@Override
public void sendAttributeChangeNotification
(Attribute oldValue, Attribute newValue)
throws MBeanException, RuntimeOperationsException {
// Calculate the class name for the change notification
String type = null;
if (newValue.getValue() != null)
type = newValue.getValue().getClass().getName();
else if (oldValue.getValue() != null)
type = oldValue.getValue().getClass().getName();
else
return; // Old and new are both null == no change
AttributeChangeNotification notification =
new AttributeChangeNotification
(this, 1, System.currentTimeMillis(),
"Attribute value has changed",
oldValue.getName(), type,
oldValue.getValue(), newValue.getValue());
sendAttributeChangeNotification(notification);
}
/**
* Send a <code>Notification</code> to all registered listeners as a
* <code>jmx.modelmbean.general</code> notification.
*
* @param notification The <code>Notification</code> that will be passed
*
* @exception MBeanException if an object initializer throws an
* exception
* @exception RuntimeOperationsException wraps IllegalArgumentException
* when the specified notification is <code>null</code> or invalid
*/
@Override
public void sendNotification(Notification notification)
throws MBeanException, RuntimeOperationsException {
if (notification == null)
throw new RuntimeOperationsException
(new IllegalArgumentException("Notification is null"),
"Notification is null");
if (generalBroadcaster == null)
return; // This means there are no registered listeners
generalBroadcaster.sendNotification(notification);
}
/**
* Send a <code>Notification</code> which contains the specified string
* as a <code>jmx.modelmbean.generic</code> notification.
*
* @param message The message string to be passed
*
* @exception MBeanException if an object initializer throws an
* exception
* @exception RuntimeOperationsException wraps IllegalArgumentException
* when the specified notification is <code>null</code> or invalid
*/
@Override
public void sendNotification(String message)
throws MBeanException, RuntimeOperationsException {
if (message == null)
throw new RuntimeOperationsException
(new IllegalArgumentException("Message is null"),
"Message is null");
Notification notification = new Notification
("jmx.modelmbean.generic", this, 1, message);
sendNotification(notification);
}
// ---------------------------------------- NotificationBroadcaster Methods
/**
* Add a notification event listener to this MBean.
*
* @param listener Listener that will receive event notifications
* @param filter Filter object used to filter event notifications
* actually delivered, or <code>null</code> for no filtering
* @param handback Handback object to be sent along with event
* notifications
*
* @exception IllegalArgumentException if the listener parameter is null
*/
@Override
public void addNotificationListener(NotificationListener listener,
NotificationFilter filter,
Object handback)
throws IllegalArgumentException {
if (listener == null)
throw new IllegalArgumentException("Listener is null");
if( log.isDebugEnabled() ) log.debug("addNotificationListener " + listener);
if (generalBroadcaster == null)
generalBroadcaster = new BaseNotificationBroadcaster();
generalBroadcaster.addNotificationListener
(listener, filter, handback);
// We'll send the attribute change notifications to all listeners ( who care )
// The normal filtering can be used.
// The problem is that there is no other way to add attribute change listeners
// to a model mbean ( AFAIK ). I suppose the spec should be fixed.
if (attributeBroadcaster == null)
attributeBroadcaster = new BaseNotificationBroadcaster();
if( log.isDebugEnabled() )
log.debug("addAttributeNotificationListener " + listener);
attributeBroadcaster.addNotificationListener
(listener, filter, handback);
}
/**
* Return an <code>MBeanNotificationInfo</code> object describing the
* notifications sent by this MBean.
*/
@Override
public MBeanNotificationInfo[] getNotificationInfo() {
// Acquire the set of application notifications
MBeanNotificationInfo current[] = getMBeanInfo().getNotifications();
MBeanNotificationInfo response[] =
new MBeanNotificationInfo[current.length + 2];
// Descriptor descriptor = null;
// Fill in entry for general notifications
// descriptor = new DescriptorSupport
// (new String[] { "name=GENERIC",
// "descriptorType=notification",
// "log=T",
// "severity=5",
// "displayName=jmx.modelmbean.generic" });
response[0] = new MBeanNotificationInfo
(new String[] { "jmx.modelmbean.generic" },
"GENERIC",
"Text message notification from the managed resource");
//descriptor);
// Fill in entry for attribute change notifications
// descriptor = new DescriptorSupport
// (new String[] { "name=ATTRIBUTE_CHANGE",
// "descriptorType=notification",
// "log=T",
// "severity=5",
// "displayName=jmx.attribute.change" });
response[1] = new MBeanNotificationInfo
(new String[] { "jmx.attribute.change" },
"ATTRIBUTE_CHANGE",
"Observed MBean attribute value has changed");
//descriptor);
// Copy remaining notifications as reported by the application
System.arraycopy(current, 0, response, 2, current.length);
return response;
}
/**
* Remove a notification event listener from this MBean.
*
* @param listener The listener to be removed (any and all registrations
* for this listener will be eliminated)
*
* @exception ListenerNotFoundException if this listener is not
* registered in the MBean
*/
@Override
public void removeNotificationListener(NotificationListener listener)
throws ListenerNotFoundException {
if (listener == null)
throw new IllegalArgumentException("Listener is null");
if (generalBroadcaster != null) {
generalBroadcaster.removeNotificationListener(listener);
}
if (attributeBroadcaster != null) {
attributeBroadcaster.removeNotificationListener(listener);
}
}
public String getModelerType() {
return resourceType;
}
public String getClassName() {
return getModelerType();
}
public ObjectName getJmxName() {
return oname;
}
public String getObjectName() {
if (oname != null) {
return oname.toString();
} else {
return null;
}
}
// -------------------- Registration --------------------
// XXX We can add some method patterns here- like setName() and
// setDomain() for code that doesn't implement the Registration
@Override
public ObjectName preRegister(MBeanServer server,
ObjectName name)
throws Exception
{
if( log.isDebugEnabled())
log.debug("preRegister " + resource + " " + name );
oname=name;
if( resource instanceof MBeanRegistration ) {
oname = ((MBeanRegistration)resource).preRegister(server, name );
}
return oname;
}
@Override
public void postRegister(Boolean registrationDone) {
if( resource instanceof MBeanRegistration ) {
((MBeanRegistration)resource).postRegister(registrationDone);
}
}
@Override
public void preDeregister() throws Exception {
if( resource instanceof MBeanRegistration ) {
((MBeanRegistration)resource).preDeregister();
}
}
@Override
public void postDeregister() {
if( resource instanceof MBeanRegistration ) {
((MBeanRegistration)resource).postDeregister();
}
}
}

View File

@@ -0,0 +1,192 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.modeler;
import java.util.ArrayList;
import java.util.Iterator;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanNotificationInfo;
import javax.management.Notification;
import javax.management.NotificationBroadcaster;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
/**
* <p>Implementation of <code>NotificationBroadcaster</code> for attribute
* change notifications. This class is used by <code>BaseModelMBean</code> to
* handle notifications of attribute change events to interested listeners.
*</p>
*
* @author Craig R. McClanahan
* @author Costin Manolache
*/
public class BaseNotificationBroadcaster implements NotificationBroadcaster {
// ----------------------------------------------------------- Constructors
// ----------------------------------------------------- Instance Variables
/**
* The set of registered <code>BaseNotificationBroadcasterEntry</code>
* entries.
*/
protected ArrayList<BaseNotificationBroadcasterEntry> entries =
new ArrayList<>();
// --------------------------------------------------------- Public Methods
/**
* Add a notification event listener to this MBean.
*
* @param listener Listener that will receive event notifications
* @param filter Filter object used to filter event notifications
* actually delivered, or <code>null</code> for no filtering
* @param handback Handback object to be sent along with event
* notifications
*
* @exception IllegalArgumentException if the listener parameter is null
*/
@Override
public void addNotificationListener(NotificationListener listener,
NotificationFilter filter,
Object handback)
throws IllegalArgumentException {
synchronized (entries) {
// Optimization to coalesce attribute name filters
if (filter instanceof BaseAttributeFilter) {
BaseAttributeFilter newFilter = (BaseAttributeFilter) filter;
for (BaseNotificationBroadcasterEntry item : entries) {
if ((item.listener == listener) &&
(item.filter != null) &&
(item.filter instanceof BaseAttributeFilter) &&
(item.handback == handback)) {
BaseAttributeFilter oldFilter =
(BaseAttributeFilter) item.filter;
String newNames[] = newFilter.getNames();
String oldNames[] = oldFilter.getNames();
if (newNames.length == 0) {
oldFilter.clear();
} else {
if (oldNames.length != 0) {
for (int i = 0; i < newNames.length; i++)
oldFilter.addAttribute(newNames[i]);
}
}
return;
}
}
}
// General purpose addition of a new entry
entries.add(new BaseNotificationBroadcasterEntry
(listener, filter, handback));
}
}
/**
* Return an <code>MBeanNotificationInfo</code> object describing the
* notifications sent by this MBean.
*/
@Override
public MBeanNotificationInfo[] getNotificationInfo() {
return new MBeanNotificationInfo[0];
}
/**
* Remove a notification event listener from this MBean.
*
* @param listener The listener to be removed (any and all registrations
* for this listener will be eliminated)
*
* @exception ListenerNotFoundException if this listener is not
* registered in the MBean
*/
@Override
public void removeNotificationListener(NotificationListener listener)
throws ListenerNotFoundException {
synchronized (entries) {
Iterator<BaseNotificationBroadcasterEntry> items =
entries.iterator();
while (items.hasNext()) {
BaseNotificationBroadcasterEntry item = items.next();
if (item.listener == listener)
items.remove();
}
}
}
/**
* Send the specified notification to all interested listeners.
*
* @param notification The notification to be sent
*/
public void sendNotification(Notification notification) {
synchronized (entries) {
for (BaseNotificationBroadcasterEntry item : entries) {
if ((item.filter != null) &&
(!item.filter.isNotificationEnabled(notification)))
continue;
item.listener.handleNotification(notification, item.handback);
}
}
}
}
/**
* Utility class representing a particular registered listener entry.
*/
class BaseNotificationBroadcasterEntry {
public BaseNotificationBroadcasterEntry(NotificationListener listener,
NotificationFilter filter,
Object handback) {
this.listener = listener;
this.filter = filter;
this.handback = handback;
}
public NotificationFilter filter = null;
public Object handback = null;
public NotificationListener listener = null;
}

View File

@@ -0,0 +1,83 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.modeler;
import java.io.Serializable;
import javax.management.MBeanFeatureInfo;
/**
* <p>Convenience base class for <code>AttributeInfo</code> and
* <code>OperationInfo</code> classes that will be used to collect configuration
* information for the <code>ModelMBean</code> beans exposed for management.</p>
*
* @author Craig R. McClanahan
*/
public class FeatureInfo implements Serializable {
static final long serialVersionUID = -911529176124712296L;
protected String description = null;
protected String name = null;
protected MBeanFeatureInfo info = null;
// all have type except Constructor
protected String type = null;
// ------------------------------------------------------------- Properties
/**
* @return the human-readable description of this feature.
*/
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
/**
* @return the name of this feature, which must be unique among features
* in the same collection.
*/
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
/**
* @return the fully qualified Java class name of this element.
*/
public String getType() {
return this.type;
}
public void setType(String type) {
this.type = type;
}
}

View File

@@ -0,0 +1,17 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
modules.digesterParseError=Error parsing registry data
modules.readDescriptorsError=Error reading descriptors

View File

@@ -0,0 +1,17 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
modules.digesterParseError=Erreur lors de l'analyse des données du registre
modules.readDescriptorsError=Erreur lors de la lecture des descripteurs

View File

@@ -0,0 +1,17 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
modules.digesterParseError=レジストリデータの解析エラー
modules.readDescriptorsError=ディスクリプタ読み取り中のエラー

View File

@@ -0,0 +1,17 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
modules.digesterParseError=레지스트리 데이터를 파싱하는 중 오류 발생
modules.readDescriptorsError=Descriptor들을 읽는 중 오류 발생

View File

@@ -0,0 +1,16 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
modules.readDescriptorsError=读取描述文件出错

View File

@@ -0,0 +1,586 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.modeler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.management.AttributeNotFoundException;
import javax.management.DynamicMBean;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanConstructorInfo;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanOperationInfo;
import javax.management.ReflectionException;
import javax.management.RuntimeOperationsException;
import javax.management.ServiceNotFoundException;
import org.apache.tomcat.util.buf.StringUtils;
import org.apache.tomcat.util.buf.StringUtils.Function;
/**
* <p>Internal configuration information for a managed bean (MBean)
* descriptor.</p>
*
* @author Craig R. McClanahan
*/
public class ManagedBean implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private static final String BASE_MBEAN = "org.apache.tomcat.util.modeler.BaseModelMBean";
// ----------------------------------------------------- Instance Variables
static final Class<?>[] NO_ARGS_PARAM_SIG = new Class[0];
private final ReadWriteLock mBeanInfoLock = new ReentrantReadWriteLock();
/**
* The <code>ModelMBeanInfo</code> object that corresponds
* to this <code>ManagedBean</code> instance.
*/
private transient volatile MBeanInfo info = null;
private Map<String,AttributeInfo> attributes = new HashMap<>();
private Map<String,OperationInfo> operations = new HashMap<>();
protected String className = BASE_MBEAN;
protected String description = null;
protected String domain = null;
protected String group = null;
protected String name = null;
private NotificationInfo notifications[] = new NotificationInfo[0];
protected String type = null;
/** Constructor. Will add default attributes.
*
*/
public ManagedBean() {
AttributeInfo ai=new AttributeInfo();
ai.setName("modelerType");
ai.setDescription("Type of the modeled resource. Can be set only once");
ai.setType("java.lang.String");
ai.setWriteable(false);
addAttribute(ai);
}
// ------------------------------------------------------------- Properties
/**
* @return the collection of attributes for this MBean.
*/
public AttributeInfo[] getAttributes() {
AttributeInfo result[] = new AttributeInfo[attributes.size()];
attributes.values().toArray(result);
return result;
}
/**
* The fully qualified name of the Java class of the MBean
* described by this descriptor. If not specified, the standard JMX
* class (<code>javax.management.modelmbean.RequiredModeLMBean</code>)
* will be utilized.
* @return the class name
*/
public String getClassName() {
return this.className;
}
public void setClassName(String className) {
mBeanInfoLock.writeLock().lock();
try {
this.className = className;
this.info = null;
} finally {
mBeanInfoLock.writeLock().unlock();
}
}
/**
* @return the human-readable description of this MBean.
*/
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
mBeanInfoLock.writeLock().lock();
try {
this.description = description;
this.info = null;
} finally {
mBeanInfoLock.writeLock().unlock();
}
}
/**
* @return the (optional) <code>ObjectName</code> domain in which
* this MBean should be registered in the MBeanServer.
*/
public String getDomain() {
return this.domain;
}
public void setDomain(String domain) {
this.domain = domain;
}
/**
* @return the (optional) group to which this MBean belongs.
*/
public String getGroup() {
return this.group;
}
public void setGroup(String group) {
this.group = group;
}
/**
* @return the name of this managed bean, which must be unique
* among all MBeans managed by a particular MBeans server.
*/
public String getName() {
return this.name;
}
public void setName(String name) {
mBeanInfoLock.writeLock().lock();
try {
this.name = name;
this.info = null;
} finally {
mBeanInfoLock.writeLock().unlock();
}
}
/**
* @return the collection of notifications for this MBean.
*/
public NotificationInfo[] getNotifications() {
return this.notifications;
}
/**
* @return the collection of operations for this MBean.
*/
public OperationInfo[] getOperations() {
OperationInfo[] result = new OperationInfo[operations.size()];
operations.values().toArray(result);
return result;
}
/**
* @return the fully qualified name of the Java class of the resource
* implementation class described by the managed bean described
* by this descriptor.
*/
public String getType() {
return this.type;
}
public void setType(String type) {
mBeanInfoLock.writeLock().lock();
try {
this.type = type;
this.info = null;
} finally {
mBeanInfoLock.writeLock().unlock();
}
}
// --------------------------------------------------------- Public Methods
/**
* Add a new attribute to the set of attributes for this MBean.
*
* @param attribute The new attribute descriptor
*/
public void addAttribute(AttributeInfo attribute) {
attributes.put(attribute.getName(), attribute);
}
/**
* Add a new notification to the set of notifications for this MBean.
*
* @param notification The new notification descriptor
*/
public void addNotification(NotificationInfo notification) {
mBeanInfoLock.writeLock().lock();
try {
NotificationInfo results[] =
new NotificationInfo[notifications.length + 1];
System.arraycopy(notifications, 0, results, 0,
notifications.length);
results[notifications.length] = notification;
notifications = results;
this.info = null;
} finally {
mBeanInfoLock.writeLock().unlock();
}
}
/**
* Add a new operation to the set of operations for this MBean.
*
* @param operation The new operation descriptor
*/
public void addOperation(OperationInfo operation) {
operations.put(createOperationKey(operation), operation);
}
/**
* Create and return a <code>ModelMBean</code> that has been
* preconfigured with the <code>ModelMBeanInfo</code> information
* for this managed bean, and is associated with the specified
* managed object instance. The returned <code>ModelMBean</code>
* will <strong>NOT</strong> have been registered with our
* <code>MBeanServer</code>.
*
* @param instance Instanced of the managed object, or <code>null</code>
* for no associated instance
* @return the MBean
* @exception InstanceNotFoundException if the managed resource
* object cannot be found
* @exception MBeanException if a problem occurs instantiating the
* <code>ModelMBean</code> instance
* @exception RuntimeOperationsException if a JMX runtime error occurs
*/
public DynamicMBean createMBean(Object instance)
throws InstanceNotFoundException,
MBeanException, RuntimeOperationsException {
BaseModelMBean mbean = null;
// Load the ModelMBean implementation class
if(getClassName().equals(BASE_MBEAN)) {
// Skip introspection
mbean = new BaseModelMBean();
} else {
Class<?> clazz = null;
Exception ex = null;
try {
clazz = Class.forName(getClassName());
} catch (Exception e) {
}
if( clazz==null ) {
try {
ClassLoader cl= Thread.currentThread().getContextClassLoader();
if ( cl != null)
clazz= cl.loadClass(getClassName());
} catch (Exception e) {
ex=e;
}
}
if( clazz==null) {
throw new MBeanException
(ex, "Cannot load ModelMBean class " + getClassName());
}
try {
// Stupid - this will set the default minfo first....
mbean = (BaseModelMBean) clazz.getConstructor().newInstance();
} catch (RuntimeOperationsException e) {
throw e;
} catch (Exception e) {
throw new MBeanException
(e, "Cannot instantiate ModelMBean of class " +
getClassName());
}
}
mbean.setManagedBean(this);
// Set the managed resource (if any)
try {
if (instance != null)
mbean.setManagedResource(instance, "ObjectReference");
} catch (InstanceNotFoundException e) {
throw e;
}
return mbean;
}
/**
* Create and return a <code>ModelMBeanInfo</code> object that
* describes this entire managed bean.
* @return the MBean info
*/
MBeanInfo getMBeanInfo() {
// Return our cached information (if any)
mBeanInfoLock.readLock().lock();
try {
if (info != null) {
return info;
}
} finally {
mBeanInfoLock.readLock().unlock();
}
mBeanInfoLock.writeLock().lock();
try {
if (info == null) {
// Create subordinate information descriptors as required
AttributeInfo attrs[] = getAttributes();
MBeanAttributeInfo attributes[] =
new MBeanAttributeInfo[attrs.length];
for (int i = 0; i < attrs.length; i++)
attributes[i] = attrs[i].createAttributeInfo();
OperationInfo opers[] = getOperations();
MBeanOperationInfo operations[] =
new MBeanOperationInfo[opers.length];
for (int i = 0; i < opers.length; i++)
operations[i] = opers[i].createOperationInfo();
NotificationInfo notifs[] = getNotifications();
MBeanNotificationInfo notifications[] =
new MBeanNotificationInfo[notifs.length];
for (int i = 0; i < notifs.length; i++)
notifications[i] = notifs[i].createNotificationInfo();
// Construct and return a new ModelMBeanInfo object
info = new MBeanInfo(getClassName(),
getDescription(),
attributes,
new MBeanConstructorInfo[] {},
operations,
notifications);
}
return info;
} finally {
mBeanInfoLock.writeLock().unlock();
}
}
/**
* Return a string representation of this managed bean.
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder("ManagedBean[");
sb.append("name=");
sb.append(name);
sb.append(", className=");
sb.append(className);
sb.append(", description=");
sb.append(description);
if (group != null) {
sb.append(", group=");
sb.append(group);
}
sb.append(", type=");
sb.append(type);
sb.append("]");
return sb.toString();
}
Method getGetter(String aname, BaseModelMBean mbean, Object resource)
throws AttributeNotFoundException, ReflectionException {
Method m = null;
AttributeInfo attrInfo = attributes.get(aname);
// Look up the actual operation to be used
if (attrInfo == null)
throw new AttributeNotFoundException(" Cannot find attribute " + aname + " for " + resource);
String getMethod = attrInfo.getGetMethod();
if (getMethod == null)
throw new AttributeNotFoundException("Cannot find attribute " + aname + " get method name");
Object object = null;
NoSuchMethodException exception = null;
try {
object = mbean;
m = object.getClass().getMethod(getMethod, NO_ARGS_PARAM_SIG);
} catch (NoSuchMethodException e) {
exception = e;
}
if (m== null && resource != null) {
try {
object = resource;
m = object.getClass().getMethod(getMethod, NO_ARGS_PARAM_SIG);
exception=null;
} catch (NoSuchMethodException e) {
exception = e;
}
}
if (exception != null)
throw new ReflectionException(exception,
"Cannot find getter method " + getMethod);
return m;
}
public Method getSetter(String aname, BaseModelMBean bean, Object resource)
throws AttributeNotFoundException, ReflectionException {
Method m = null;
AttributeInfo attrInfo = attributes.get(aname);
if (attrInfo == null)
throw new AttributeNotFoundException(" Cannot find attribute " + aname);
// Look up the actual operation to be used
String setMethod = attrInfo.getSetMethod();
if (setMethod == null)
throw new AttributeNotFoundException("Cannot find attribute " + aname + " set method name");
String argType=attrInfo.getType();
Class<?> signature[] =
new Class[] { BaseModelMBean.getAttributeClass( argType ) };
Object object = null;
NoSuchMethodException exception = null;
try {
object = bean;
m = object.getClass().getMethod(setMethod, signature);
} catch (NoSuchMethodException e) {
exception = e;
}
if (m == null && resource != null) {
try {
object = resource;
m = object.getClass().getMethod(setMethod, signature);
exception=null;
} catch (NoSuchMethodException e) {
exception = e;
}
}
if (exception != null)
throw new ReflectionException(exception,
"Cannot find setter method " + setMethod +
" " + resource);
return m;
}
public Method getInvoke(String aname, Object[] params, String[] signature, BaseModelMBean bean, Object resource)
throws MBeanException, ReflectionException {
Method method = null;
if (params == null)
params = new Object[0];
if (signature == null)
signature = new String[0];
if (params.length != signature.length)
throw new RuntimeOperationsException(
new IllegalArgumentException(
"Inconsistent arguments and signature"),
"Inconsistent arguments and signature");
// Acquire the ModelMBeanOperationInfo information for
// the requested operation
OperationInfo opInfo =
operations.get(createOperationKey(aname, signature));
if (opInfo == null)
throw new MBeanException(new ServiceNotFoundException(
"Cannot find operation " + aname),
"Cannot find operation " + aname);
// Prepare the signature required by Java reflection APIs
// FIXME - should we use the signature from opInfo?
Class<?> types[] = new Class[signature.length];
for (int i = 0; i < signature.length; i++) {
types[i] = BaseModelMBean.getAttributeClass(signature[i]);
}
// Locate the method to be invoked, either in this MBean itself
// or in the corresponding managed resource
// FIXME - Accessible methods in superinterfaces?
Object object = null;
Exception exception = null;
try {
object = bean;
method = object.getClass().getMethod(aname, types);
} catch (NoSuchMethodException e) {
exception = e;
}
try {
if ((method == null) && (resource != null)) {
object = resource;
method = object.getClass().getMethod(aname, types);
}
} catch (NoSuchMethodException e) {
exception = e;
}
if (method == null) {
throw new ReflectionException(exception, "Cannot find method "
+ aname + " with this signature");
}
return method;
}
private String createOperationKey(OperationInfo operation) {
StringBuilder key = new StringBuilder(operation.getName());
key.append('(');
StringUtils.join(operation.getSignature(), ',', new Function<ParameterInfo>() {
@Override public String apply(ParameterInfo t) { return t.getType(); }}, key);
key.append(')');
return key.toString().intern();
}
private String createOperationKey(String methodName, String[] parameterTypes) {
StringBuilder key = new StringBuilder(methodName);
key.append('(');
StringUtils.join(parameterTypes, ',', key);
key.append(')');
return key.toString().intern();
}
}

View File

@@ -0,0 +1,156 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.modeler;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.management.MBeanNotificationInfo;
/**
* <p>Internal configuration information for a <code>Notification</code>
* descriptor.</p>
*
* @author Craig R. McClanahan
*/
public class NotificationInfo extends FeatureInfo {
static final long serialVersionUID = -6319885418912650856L;
// ----------------------------------------------------- Instance Variables
/**
* The <code>ModelMBeanNotificationInfo</code> object that corresponds
* to this <code>NotificationInfo</code> instance.
*/
transient MBeanNotificationInfo info = null;
protected String notifTypes[] = new String[0];
protected final ReadWriteLock notifTypesLock = new ReentrantReadWriteLock();
// ------------------------------------------------------------- Properties
/**
* Override the <code>description</code> property setter.
*
* @param description The new description
*/
@Override
public void setDescription(String description) {
super.setDescription(description);
this.info = null;
}
/**
* Override the <code>name</code> property setter.
*
* @param name The new name
*/
@Override
public void setName(String name) {
super.setName(name);
this.info = null;
}
/**
* @return the set of notification types for this MBean.
*/
public String[] getNotifTypes() {
Lock readLock = notifTypesLock.readLock();
readLock.lock();
try {
return this.notifTypes;
} finally {
readLock.unlock();
}
}
// --------------------------------------------------------- Public Methods
/**
* Add a new notification type to the set managed by an MBean.
*
* @param notifType The new notification type
*/
public void addNotifType(String notifType) {
Lock writeLock = notifTypesLock.writeLock();
writeLock.lock();
try {
String results[] = new String[notifTypes.length + 1];
System.arraycopy(notifTypes, 0, results, 0, notifTypes.length);
results[notifTypes.length] = notifType;
notifTypes = results;
this.info = null;
} finally {
writeLock.unlock();
}
}
/**
* Create and return a <code>ModelMBeanNotificationInfo</code> object that
* corresponds to the attribute described by this instance.
* @return the notification info
*/
public MBeanNotificationInfo createNotificationInfo() {
// Return our cached information (if any)
if (info != null)
return info;
// Create and return a new information object
info = new MBeanNotificationInfo
(getNotifTypes(), getName(), getDescription());
//Descriptor descriptor = info.getDescriptor();
//addFields(descriptor);
//info.setDescriptor(descriptor);
return info;
}
/**
* Return a string representation of this notification descriptor.
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder("NotificationInfo[");
sb.append("name=");
sb.append(name);
sb.append(", description=");
sb.append(description);
sb.append(", notifTypes=");
Lock readLock = notifTypesLock.readLock();
readLock.lock();
try {
sb.append(notifTypes.length);
} finally {
readLock.unlock();
}
sb.append("]");
return sb.toString();
}
}

View File

@@ -0,0 +1,172 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.modeler;
import java.util.Locale;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
/**
* <p>Internal configuration information for an <code>Operation</code>
* descriptor.</p>
*
* @author Craig R. McClanahan
*/
public class OperationInfo extends FeatureInfo {
static final long serialVersionUID = 4418342922072614875L;
// ----------------------------------------------------------- Constructors
/**
* Standard zero-arguments constructor.
*/
public OperationInfo() {
super();
}
// ----------------------------------------------------- Instance Variables
protected String impact = "UNKNOWN";
protected String role = "operation";
protected final ReadWriteLock parametersLock = new ReentrantReadWriteLock();
protected ParameterInfo parameters[] = new ParameterInfo[0];
// ------------------------------------------------------------- Properties
/**
* @return the "impact" of this operation, which should be
* a (case-insensitive) string value "ACTION", "ACTION_INFO",
* "INFO", or "UNKNOWN".
*/
public String getImpact() {
return this.impact;
}
public void setImpact(String impact) {
if (impact == null)
this.impact = null;
else
this.impact = impact.toUpperCase(Locale.ENGLISH);
}
/**
* @return the role of this operation ("getter", "setter", "operation", or
* "constructor").
*/
public String getRole() {
return this.role;
}
public void setRole(String role) {
this.role = role;
}
/**
* @return the fully qualified Java class name of the return type for this
* operation.
*/
public String getReturnType() {
if(type == null) {
type = "void";
}
return type;
}
public void setReturnType(String returnType) {
this.type = returnType;
}
/**
* @return the set of parameters for this operation.
*/
public ParameterInfo[] getSignature() {
Lock readLock = parametersLock.readLock();
readLock.lock();
try {
return this.parameters;
} finally {
readLock.unlock();
}
}
// --------------------------------------------------------- Public Methods
/**
* Add a new parameter to the set of arguments for this operation.
*
* @param parameter The new parameter descriptor
*/
public void addParameter(ParameterInfo parameter) {
Lock writeLock = parametersLock.writeLock();
writeLock.lock();
try {
ParameterInfo results[] = new ParameterInfo[parameters.length + 1];
System.arraycopy(parameters, 0, results, 0, parameters.length);
results[parameters.length] = parameter;
parameters = results;
this.info = null;
} finally {
writeLock.unlock();
}
}
/**
* Create and return a <code>ModelMBeanOperationInfo</code> object that
* corresponds to the attribute described by this instance.
* @return the operation info
*/
MBeanOperationInfo createOperationInfo() {
// Return our cached information (if any)
if (info == null) {
// Create and return a new information object
int impact = MBeanOperationInfo.UNKNOWN;
if ("ACTION".equals(getImpact()))
impact = MBeanOperationInfo.ACTION;
else if ("ACTION_INFO".equals(getImpact()))
impact = MBeanOperationInfo.ACTION_INFO;
else if ("INFO".equals(getImpact()))
impact = MBeanOperationInfo.INFO;
info = new MBeanOperationInfo(getName(), getDescription(),
getMBeanParameterInfo(),
getReturnType(), impact);
}
return (MBeanOperationInfo)info;
}
protected MBeanParameterInfo[] getMBeanParameterInfo() {
ParameterInfo params[] = getSignature();
MBeanParameterInfo parameters[] =
new MBeanParameterInfo[params.length];
for (int i = 0; i < params.length; i++)
parameters[i] = params[i].createParameterInfo();
return parameters;
}
}

View File

@@ -0,0 +1,57 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.modeler;
import javax.management.MBeanParameterInfo;
/**
* <p>Internal configuration information for a <code>Parameter</code>
* descriptor.</p>
*
* @author Craig R. McClanahan
*/
public class ParameterInfo extends FeatureInfo {
static final long serialVersionUID = 2222796006787664020L;
// ----------------------------------------------------------- Constructors
/**
* Standard zero-arguments constructor.
*/
public ParameterInfo() {
super();
}
/**
* Create and return a <code>MBeanParameterInfo</code> object that
* corresponds to the parameter described by this instance.
* @return a parameter info
*/
public MBeanParameterInfo createParameterInfo() {
// Return our cached information (if any)
if (info == null) {
info = new MBeanParameterInfo
(getName(), getType(), getDescription());
}
return (MBeanParameterInfo)info;
}
}

View File

@@ -0,0 +1,748 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.modeler;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.management.ManagementFactory;
import java.net.URL;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import javax.management.DynamicMBean;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanRegistration;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.modeler.modules.ModelerSource;
import org.apache.tomcat.util.res.StringManager;
/*
Issues:
- exceptions - too many "throws Exception"
- double check the interfaces
- start removing the use of the experimental methods in tomcat, then remove
the methods ( before 1.1 final )
- is the security enough to prevent Registry being used to avoid the
permission checks in the mbean server ?
*/
/**
* Registry for modeler MBeans.
*
* This is the main entry point into modeler. It provides methods to create and
* manipulate model mbeans and simplify their use.
*
* This class is itself an mbean.
*
* IMPORTANT: public methods not marked with @since x.x are experimental or
* internal. Should not be used.
*
* @author Craig R. McClanahan
* @author Costin Manolache
*/
public class Registry implements RegistryMBean, MBeanRegistration {
/**
* The Log instance to which we will write our log messages.
*/
private static final Log log = LogFactory.getLog(Registry.class);
private static final StringManager sm = StringManager.getManager(Registry.class);
// Support for the factory methods
/**
* The registry instance created by our factory method the first time it is
* called.
*/
private static Registry registry = null;
// Per registry fields
/**
* The <code>MBeanServer</code> instance that we will use to register
* management beans.
*/
private volatile MBeanServer server = null;
private final Object serverLock = new Object();
/**
* The set of ManagedBean instances for the beans this registry knows about,
* keyed by name.
*/
private HashMap<String,ManagedBean> descriptors = new HashMap<>();
/** List of managed beans, keyed by class name
*/
private HashMap<String,ManagedBean> descriptorsByClass = new HashMap<>();
// map to avoid duplicated searching or loading descriptors
private HashMap<String,URL> searchedPaths = new HashMap<>();
private Object guard;
// Id - small ints to use array access. No reset on stop()
// Used for notifications
private final Hashtable<String,Hashtable<String,Integer>> idDomains =
new Hashtable<>();
private final Hashtable<String,int[]> ids = new Hashtable<>();
// ----------------------------------------------------------- Constructors
public Registry() {
super();
}
// -------------------- Static methods --------------------
// Factories
/**
* Factory method to create (if necessary) and return our
* <code>Registry</code> instance.
*
* @param key Unused
* @param guard Prevent access to the registry by untrusted components
*
* @return the registry
* @since 1.1
*/
public static synchronized Registry getRegistry(Object key, Object guard) {
if (registry == null) {
registry = new Registry();
}
if (registry.guard != null && registry.guard != guard) {
return null;
}
return registry;
}
/** Lifecycle method - clean up the registry metadata.
* Called from resetMetadata().
*
* @since 1.1
*/
@Override
public void stop() {
descriptorsByClass = new HashMap<>();
descriptors = new HashMap<>();
searchedPaths = new HashMap<>();
}
/**
* Register a bean by creating a modeler mbean and adding it to the
* MBeanServer.
*
* If metadata is not loaded, we'll look up and read a file named
* "mbeans-descriptors.ser" or "mbeans-descriptors.xml" in the same package
* or parent.
*
* If the bean is an instance of DynamicMBean. it's metadata will be
* converted to a model mbean and we'll wrap it - so modeler services will
* be supported
*
* If the metadata is still not found, introspection will be used to extract
* it automatically.
*
* If an mbean is already registered under this name, it'll be first
* unregistered.
*
* If the component implements MBeanRegistration, the methods will be
* called. If the method has a method "setRegistry" that takes a
* RegistryMBean as parameter, it'll be called with the current registry.
*
*
* @param bean Object to be registered
* @param oname Name used for registration
* @param type The type of the mbean, as declared in mbeans-descriptors. If
* null, the name of the class will be used. This can be used as
* a hint or by subclasses.
* @throws Exception if a registration error occurred
* @since 1.1
*/
@Override
public void registerComponent(Object bean, String oname, String type) throws Exception {
registerComponent(bean, new ObjectName(oname), type);
}
/**
* Unregister a component. We'll first check if it is registered, and mask
* all errors. This is mostly a helper.
*
* @param oname Name used for unregistration
*
* @since 1.1
*/
@Override
public void unregisterComponent(String oname) {
try {
unregisterComponent(new ObjectName(oname));
} catch (MalformedObjectNameException e) {
log.info("Error creating object name " + e );
}
}
/**
* Invoke a operation on a list of mbeans. Can be used to implement
* lifecycle operations.
*
* @param mbeans list of ObjectName on which we'll invoke the operations
* @param operation Name of the operation ( init, start, stop, etc)
* @param failFirst If false, exceptions will be ignored
* @throws Exception Error invoking operation
* @since 1.1
*/
@Override
public void invoke(List<ObjectName> mbeans, String operation, boolean failFirst)
throws Exception {
if (mbeans == null) {
return;
}
for (ObjectName current : mbeans) {
try {
if (current == null) {
continue;
}
if (getMethodInfo(current, operation) == null) {
continue;
}
getMBeanServer().invoke(current, operation, new Object[] {}, new String[] {});
} catch (Exception t) {
if (failFirst)
throw t;
log.info("Error initializing " + current + " " + t.toString());
}
}
}
// -------------------- ID registry --------------------
/**
* Return an int ID for faster access. Will be used for notifications
* and for other operations we want to optimize.
*
* @param domain Namespace
* @param name Type of the notification
* @return A unique id for the domain:name combination
* @since 1.1
*/
@Override
public synchronized int getId(String domain, String name) {
if (domain == null) {
domain = "";
}
Hashtable<String, Integer> domainTable = idDomains.get(domain);
if (domainTable == null) {
domainTable = new Hashtable<>();
idDomains.put(domain, domainTable);
}
if (name == null) {
name = "";
}
Integer i = domainTable.get(name);
if (i != null) {
return i.intValue();
}
int id[] = ids.get(domain);
if (id == null) {
id = new int[1];
ids.put(domain, id);
}
int code = id[0]++;
domainTable.put(name, Integer.valueOf(code));
return code;
}
// -------------------- Metadata --------------------
// methods from 1.0
/**
* Add a new bean metadata to the set of beans known to this registry. This
* is used by internal components.
*
* @param bean The managed bean to be added
* @since 1.0
*/
public void addManagedBean(ManagedBean bean) {
// XXX Use group + name
descriptors.put(bean.getName(), bean);
if (bean.getType() != null) {
descriptorsByClass.put(bean.getType(), bean);
}
}
/**
* Find and return the managed bean definition for the specified bean name,
* if any; otherwise return <code>null</code>.
*
* @param name Name of the managed bean to be returned. Since 1.1, both
* short names or the full name of the class can be used.
* @return the managed bean
* @since 1.0
*/
public ManagedBean findManagedBean(String name) {
// XXX Group ?? Use Group + Type
ManagedBean mb = descriptors.get(name);
if (mb == null)
mb = descriptorsByClass.get(name);
return mb;
}
// -------------------- Helpers --------------------
/**
* Get the type of an attribute of the object, from the metadata.
*
* @param oname The bean name
* @param attName The attribute name
* @return null if metadata about the attribute is not found
* @since 1.1
*/
public String getType(ObjectName oname, String attName) {
String type = null;
MBeanInfo info = null;
try {
info = getMBeanServer().getMBeanInfo(oname);
} catch (Exception e) {
log.info( "Can't find metadata for object" + oname );
return null;
}
MBeanAttributeInfo attInfo[] = info.getAttributes();
for (int i = 0; i < attInfo.length; i++) {
if (attName.equals(attInfo[i].getName())) {
type = attInfo[i].getType();
return type;
}
}
return null;
}
/**
* Find the operation info for a method
*
* @param oname The bean name
* @param opName The operation name
* @return the operation info for the specified operation
*/
public MBeanOperationInfo getMethodInfo(ObjectName oname, String opName) {
MBeanInfo info = null;
try {
info = getMBeanServer().getMBeanInfo(oname);
} catch (Exception e) {
log.info( "Can't find metadata " + oname );
return null;
}
MBeanOperationInfo attInfo[] = info.getOperations();
for (int i = 0; i < attInfo.length; i++) {
if (opName.equals(attInfo[i].getName())) {
return attInfo[i];
}
}
return null;
}
/**
* Find the operation info for a method.
*
* @param oname The bean name
* @param opName The operation name
* @param argCount The number of arguments to the method
* @return the operation info for the specified operation
* @throws InstanceNotFoundException If the object name is not bound to an MBean
*/
public MBeanOperationInfo getMethodInfo(ObjectName oname, String opName, int argCount)
throws InstanceNotFoundException
{
MBeanInfo info = null;
try {
info = getMBeanServer().getMBeanInfo(oname);
} catch (InstanceNotFoundException infe) {
throw infe;
} catch (Exception e) {
log.warn(sm.getString("registry.noMetadata", oname), e);
return null;
}
MBeanOperationInfo attInfo[] = info.getOperations();
for (int i = 0; i < attInfo.length; i++) {
if (opName.equals(attInfo[i].getName())
&& argCount == attInfo[i].getSignature().length) {
return attInfo[i];
}
}
return null;
}
/**
* Unregister a component. This is just a helper that avoids exceptions by
* checking if the mbean is already registered
*
* @param oname The bean name
*/
public void unregisterComponent(ObjectName oname) {
try {
if (oname != null && getMBeanServer().isRegistered(oname)) {
getMBeanServer().unregisterMBean(oname);
}
} catch (Throwable t) {
log.error("Error unregistering mbean", t);
}
}
/**
* Factory method to create (if necessary) and return our
* <code>MBeanServer</code> instance.
*
* @return the MBean server
*/
public MBeanServer getMBeanServer() {
if (server == null) {
synchronized (serverLock) {
if (server == null) {
long t1 = System.currentTimeMillis();
if (MBeanServerFactory.findMBeanServer(null).size() > 0) {
server = MBeanServerFactory.findMBeanServer(null).get(0);
if (log.isDebugEnabled()) {
log.debug("Using existing MBeanServer " + (System.currentTimeMillis() - t1));
}
} else {
server = ManagementFactory.getPlatformMBeanServer();
if (log.isDebugEnabled()) {
log.debug("Creating MBeanServer" + (System.currentTimeMillis() - t1));
}
}
}
}
}
return server;
}
/**
* Find or load metadata.
*
* @param bean The bean
* @param beanClass The bean class
* @param type The registry type
* @return the managed bean
* @throws Exception An error occurred
*/
public ManagedBean findManagedBean(Object bean, Class<?> beanClass, String type)
throws Exception {
if (bean != null && beanClass == null) {
beanClass = bean.getClass();
}
if (type == null) {
type = beanClass.getName();
}
// first look for existing descriptor
ManagedBean managed = findManagedBean(type);
// Search for a descriptor in the same package
if (managed == null) {
// check package and parent packages
if (log.isDebugEnabled()) {
log.debug("Looking for descriptor ");
}
findDescriptor(beanClass, type);
managed = findManagedBean(type);
}
// Still not found - use introspection
if (managed == null) {
if (log.isDebugEnabled()) {
log.debug("Introspecting ");
}
// introspection
load("MbeansDescriptorsIntrospectionSource", beanClass, type);
managed = findManagedBean(type);
if (managed == null) {
log.warn( "No metadata found for " + type );
return null;
}
managed.setName(type);
addManagedBean(managed);
}
return managed;
}
/**
* EXPERIMENTAL Convert a string to object, based on type. Used by several
* components. We could provide some pluggability. It is here to keep things
* consistent and avoid duplication in other tasks
*
* @param type Fully qualified class name of the resulting value
* @param value String value to be converted
* @return Converted value
*/
public Object convertValue(String type, String value) {
Object objValue = value;
if (type == null || "java.lang.String".equals(type)) {
// string is default
objValue = value;
} else if ("javax.management.ObjectName".equals(type) || "ObjectName".equals(type)) {
try {
objValue = new ObjectName(value);
} catch (MalformedObjectNameException e) {
return null;
}
} else if ("java.lang.Integer".equals(type) || "int".equals(type)) {
objValue = Integer.valueOf(value);
} else if ("java.lang.Long".equals(type) || "long".equals(type)) {
objValue = Long.valueOf(value);
} else if ("java.lang.Boolean".equals(type) || "boolean".equals(type)) {
objValue = Boolean.valueOf(value);
}
return objValue;
}
/**
* Experimental. Load descriptors.
*
* @param sourceType The source type
* @param source The bean
* @param param A type to load
* @return List of descriptors
* @throws Exception Error loading descriptors
*/
public List<ObjectName> load(String sourceType, Object source, String param) throws Exception {
if (log.isTraceEnabled()) {
log.trace("load " + source);
}
String location = null;
String type = null;
Object inputsource = null;
if (source instanceof URL) {
URL url = (URL) source;
location = url.toString();
type = param;
inputsource = url.openStream();
if (sourceType == null && location.endsWith(".xml")) {
sourceType = "MbeansDescriptorsDigesterSource";
}
} else if (source instanceof File) {
location = ((File) source).getAbsolutePath();
inputsource = new FileInputStream((File) source);
type = param;
if (sourceType == null && location.endsWith(".xml")) {
sourceType = "MbeansDescriptorsDigesterSource";
}
} else if (source instanceof InputStream) {
type = param;
inputsource = source;
} else if (source instanceof Class<?>) {
location = ((Class<?>) source).getName();
type = param;
inputsource = source;
if (sourceType == null) {
sourceType = "MbeansDescriptorsIntrospectionSource";
}
}
if (sourceType == null) {
sourceType = "MbeansDescriptorsDigesterSource";
}
ModelerSource ds = getModelerSource(sourceType);
List<ObjectName> mbeans = ds.loadDescriptors(this, type, inputsource);
return mbeans;
}
/**
* Register a component
*
* @param bean The bean
* @param oname The object name
* @param type The registry type
* @throws Exception Error registering component
*/
public void registerComponent(Object bean, ObjectName oname, String type) throws Exception {
if (log.isDebugEnabled()) {
log.debug("Managed= " + oname);
}
if (bean == null) {
log.error("Null component " + oname );
return;
}
try {
if (type == null) {
type = bean.getClass().getName();
}
ManagedBean managed = findManagedBean(null, bean.getClass(), type);
// The real mbean is created and registered
DynamicMBean mbean = managed.createMBean(bean);
if (getMBeanServer().isRegistered(oname)) {
if (log.isDebugEnabled()) {
log.debug("Unregistering existing component " + oname);
}
getMBeanServer().unregisterMBean(oname);
}
getMBeanServer().registerMBean(mbean, oname);
} catch (Exception ex) {
log.error("Error registering " + oname, ex );
throw ex;
}
}
/**
* Lookup the component descriptor in the package and in the parent
* packages.
*
* @param packageName The package name
* @param classLoader The class loader
*/
public void loadDescriptors(String packageName, ClassLoader classLoader) {
String res = packageName.replace('.', '/');
if (log.isTraceEnabled()) {
log.trace("Finding descriptor " + res);
}
if (searchedPaths.get(packageName) != null) {
return;
}
String descriptors = res + "/mbeans-descriptors.xml";
URL dURL = classLoader.getResource(descriptors);
if (dURL == null) {
return;
}
log.debug("Found " + dURL);
searchedPaths.put(packageName, dURL);
try {
load("MbeansDescriptorsDigesterSource", dURL, null);
} catch(Exception ex ) {
log.error("Error loading " + dURL);
}
}
/**
* Lookup the component descriptor in the package and in the parent
* packages.
*/
private void findDescriptor(Class<?> beanClass, String type) {
if (type == null) {
type = beanClass.getName();
}
ClassLoader classLoader = null;
if (beanClass != null) {
classLoader = beanClass.getClassLoader();
}
if (classLoader == null) {
classLoader = Thread.currentThread().getContextClassLoader();
}
if (classLoader == null) {
classLoader = this.getClass().getClassLoader();
}
String className = type;
String pkg = className;
while (pkg.indexOf(".") > 0) {
int lastComp = pkg.lastIndexOf(".");
if (lastComp <= 0)
return;
pkg = pkg.substring(0, lastComp);
if (searchedPaths.get(pkg) != null) {
return;
}
loadDescriptors(pkg, classLoader);
}
}
private ModelerSource getModelerSource(String type) throws Exception {
if (type == null)
type = "MbeansDescriptorsDigesterSource";
if (!type.contains(".")) {
type = "org.apache.tomcat.util.modeler.modules." + type;
}
Class<?> c = Class.forName(type);
ModelerSource ds = (ModelerSource) c.getConstructor().newInstance();
return ds;
}
// -------------------- Registration --------------------
@Override
public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception {
synchronized (serverLock) {
this.server = server;
}
return name;
}
@Override
public void postRegister(Boolean registrationDone) {
}
@Override
public void preDeregister() throws Exception {
}
@Override
public void postDeregister() {
}
}

View File

@@ -0,0 +1,120 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.modeler;
import java.util.List;
import javax.management.ObjectName;
/**
* Interface for modeler MBeans.
*
* This is the main entry point into modeler. It provides methods to create
* and manipulate model mbeans and simplify their use.
*
* Starting with version 1.1, this is no longer a singleton and the static
* methods are strongly deprecated. In a container environment we can expect
* different applications to use different registries.
*
* @author Craig R. McClanahan
* @author Costin Manolache
*
* @since 1.1
*/
public interface RegistryMBean {
/**
* Invoke an operation on a set of mbeans.
*
* @param mbeans List of ObjectNames
* @param operation Operation to perform. Typically "init" "start" "stop" or "destroy"
* @param failFirst Behavior in case of exceptions - if false we'll ignore
* errors
* @throws Exception Error invoking operation
*/
public void invoke(List<ObjectName> mbeans, String operation, boolean failFirst)
throws Exception;
/**
* Register a bean by creating a modeler mbean and adding it to the
* MBeanServer.
*
* If metadata is not loaded, we'll look up and read a file named
* "mbeans-descriptors.ser" or "mbeans-descriptors.xml" in the same package
* or parent.
*
* If the bean is an instance of DynamicMBean. it's metadata will be converted
* to a model mbean and we'll wrap it - so modeler services will be supported
*
* If the metadata is still not found, introspection will be used to extract
* it automatically.
*
* If an mbean is already registered under this name, it'll be first
* unregistered.
*
* If the component implements MBeanRegistration, the methods will be called.
* If the method has a method "setRegistry" that takes a RegistryMBean as
* parameter, it'll be called with the current registry.
*
*
* @param bean Object to be registered
* @param oname Name used for registration
* @param type The type of the mbean, as declared in mbeans-descriptors. If
* null, the name of the class will be used. This can be used as a hint or
* by subclasses.
* @throws Exception Error registering MBean
*
* @since 1.1
*/
public void registerComponent(Object bean, String oname, String type)
throws Exception;
/**
* Unregister a component. We'll first check if it is registered,
* and mask all errors. This is mostly a helper.
*
* @param oname The name used by the bean
*
* @since 1.1
*/
public void unregisterComponent(String oname);
/**
* Return an int ID for faster access. Will be used for notifications
* and for other operations we want to optimize.
*
* @param domain Namespace
* @param name Type of the notification
* @return A unique id for the domain:name combination
* @since 1.1
*/
public int getId(String domain, String name);
/**
* Reset all metadata cached by this registry. Should be called
* to support reloading. Existing mbeans will not be affected or modified.
*
* It will be called automatically if the Registry is unregistered.
* @since 1.1
*/
public void stop();
}

View File

@@ -0,0 +1,34 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.modeler;
public class Util {
private Util() {
// Utility class. Hide default constructor.
}
public static boolean objectNameValueNeedsQuote(String input) {
for (int i = 0; i < input.length(); i++) {
char ch = input.charAt(i);
if (ch == ',' || ch == '=' || ch == ':' || ch == '*' || ch == '?') {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,247 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!--
DTD for the Model MBeans Configuration File
To support validation of your configuration file, include the following
DOCTYPE element at the beginning (after the "xml" declaration):
<!DOCTYPE mbeans-descriptors PUBLIC
"-//Apache Software Foundation//DTD Model MBeans Configuration File"
"http://jakarta.apache.org/commons/dtds/mbeans-descriptors.dtd">
-->
<!-- ========== Defined Types ============================================= -->
<!-- A "Boolean" is the string representation of a boolean (true or false)
variable.
-->
<!ENTITY % Boolean "(true|false|yes|no)">
<!-- A "ClassName" is the fully qualified name of a Java class that is
instantiated to provide the functionality of the enclosing element.
-->
<!ENTITY % ClassName "CDATA">
<!-- A "MethodName" is the name of a constructor or method, which must
be legal according to the syntax requirements of the Java language.
-->
<!ENTITY % MethodName "CDATA">
<!-- A "VariableName" is the name of a variable or parameter, which must
be legal according to the syntax requirements of the Java language.
-->
<!ENTITY % VariableName "CDATA">
<!-- ========== Element Definitions ======================================= -->
<!-- The "mbeans-descriptors" element is the root of the configuration file
hierarchy, and contains nested elements for all of the other
configuration settings. Remaining element definitions are listed
in alphabetical order.
-->
<!ELEMENT mbeans-descriptors (mbean*)>
<!ATTLIST mbeans-descriptors id ID #IMPLIED>
<!-- The "attribute" element describes a JavaBeans property of an MBean.
The following attributes are supported:
description Human-readable description of this attribute.
displayName Display name of this attribute.
getMethod Name of the property getter method, if it does
not follow standard JavaBeans naming patterns.
is Boolean value indicating whether or not this
attribute is a boolean with an "is" getter method.
By default, this is set to "false".
name Name of this JavaBeans property, conforming to
standard naming design patterns.
readable Boolean value indicating whether or not this
attribute is readable by management applications.
By default, this is set to "true".
setMethod Name of the property setter method, if it does
not follow standard JavaBeans naming patterns.
type Fully qualified Java class name of this attribute.
writeable Boolean value indicating whether or not this
attribute is writeable by management applications.
By default, this is set to "true".
-->
<!ELEMENT attribute (descriptor?)>
<!ATTLIST attribute id ID #IMPLIED>
<!ATTLIST attribute description CDATA #IMPLIED>
<!ATTLIST attribute displayName CDATA #IMPLIED>
<!ATTLIST attribute getMethod %MethodName; #IMPLIED>
<!ATTLIST attribute is %Boolean; #IMPLIED>
<!ATTLIST attribute name %VariableName; #IMPLIED>
<!ATTLIST attribute readable %Boolean; #IMPLIED>
<!ATTLIST attribute setMethod %MethodName; #IMPLIED>
<!ATTLIST attribute type %ClassName; #IMPLIED>
<!ATTLIST attribute writeable %Boolean; #IMPLIED>
<!-- The "constructor" element describes a public constructor for the
underlying actual class. It may contain nested "parameter" elements
for the various arguments to this constructor. The following attributes
are supported:
displayName Display name of this constructor.
name Name of this constructor (by Java convention, this must
be the same as the base class name).
-->
<!ELEMENT constructor (descriptor?, parameter*)>
<!ATTLIST constructor id ID #IMPLIED>
<!ATTLIST constructor displayName CDATA #IMPLIED>
<!ATTLIST constructor name %VariableName; #IMPLIED>
<!-- The "descriptor" element groups a set of descriptor fields whose
values will be included in the Descriptor for the corresponding
metadata info classes.
-->
<!ELEMENT descriptor (field*)>
<!ATTLIST descriptor id ID #IMPLIED>
<!-- The "field" element represents a single name/value pair that will
be included in the Descriptor corresponding to our enclosing
"descriptor" element. The following attributes are supported:
name Field name of the field to be included
value Field value of the field to be included
(will be stored as a String)
-->
<!ELEMENT field EMPTY>
<!ATTLIST field id ID #IMPLIED>
<!ATTLIST field name CDATA #REQUIRED>
<!ATTLIST field value CDATA #REQUIRED>
<!-- The "mbean" element describes a particular JMX ModelMBean implementation,
including the information necessary to construct the corresponding
ModelMBeanInfo structures. The following attributes are supported:
className Fully qualified Java class name of the ModelMBean
implementation class. If not specified, the standard
implementation provided by JMX will be utilized.
description Human-readable description of this managed bean.
domain The JMX MBeanServer domain in which the ModelMBean
created by this managed bean should be registered,
when creating its ObjectName.
group Optional name of a "grouping classification" that can
be used to select groups of similar MBean implementation
classes.
name Unique name of this MBean (normally corresponds to the
base class name of the corresponding server component).
type Fully qualified Java class name of the underlying
managed resource implementation class.
-->
<!ELEMENT mbean (descriptor?, attribute*, constructor*, notification*, operation*)>
<!ATTLIST mbean id ID #IMPLIED>
<!ATTLIST mbean className %ClassName; #IMPLIED>
<!ATTLIST mbean description CDATA #IMPLIED>
<!ATTLIST mbean domain CDATA #IMPLIED>
<!ATTLIST mbean group CDATA #IMPLIED>
<!ATTLIST mbean name %MethodName; #IMPLIED>
<!ATTLIST mbean type %ClassName; #IMPLIED>
<!-- The "notification" element describes the notification types that are
generated by a particular managed bean. The following attributes
are supported:
description Human-readable description of these notification events.
name Name of this set of notification event types.
-->
<!ELEMENT notification (descriptor?, notification-type*)>
<!ATTLIST notification id ID #IMPLIED>
<!ATTLIST notification description CDATA #IMPLIED>
<!ATTLIST notification name %VariableName; #IMPLIED>
<!-- The nested content of the "notification-type" element is the event string
of an event that can be emitted by this MBean.
-->
<!ELEMENT notification-type (#PCDATA)>
<!ATTLIST notification-type id ID #IMPLIED>
<!-- The "operation" element describes a the signature of a public method
that is accessible to management applications. The following attributes
are supported:
description Human-readable description of this operation.
impact Indication of the impact of this method:
ACTION (write like), ACTION-INFO (write+read like)
INFO (read like), or UNKNOWN.
name Name of this public method.
returnType Fully qualified Java class name of the return
type of this method.
-->
<!ELEMENT operation (descriptor?, parameter*)>
<!ATTLIST operation id ID #IMPLIED>
<!ATTLIST operation description CDATA #IMPLIED>
<!ATTLIST operation impact CDATA #IMPLIED>
<!ATTLIST operation name %VariableName; #IMPLIED>
<!ATTLIST operation returnType %ClassName; #IMPLIED>
<!-- The "parameter" element describes a single argument that will be passed
to a constructor or operation. The following attributes are supported:
description Human-readable description of this parameter.
name Java language name of this parameter.
type Fully qualified Java class name of this parameter.
-->
<!ELEMENT parameter EMPTY>
<!ATTLIST parameter id ID #IMPLIED>
<!ATTLIST parameter description CDATA #IMPLIED>
<!ATTLIST parameter name %VariableName; #IMPLIED>
<!ATTLIST parameter type %ClassName; #IMPLIED>

View File

@@ -0,0 +1,182 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.modeler.modules;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import javax.management.ObjectName;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.digester.Digester;
import org.apache.tomcat.util.modeler.ManagedBean;
import org.apache.tomcat.util.modeler.Registry;
public class MbeansDescriptorsDigesterSource extends ModelerSource
{
private static final Log log =
LogFactory.getLog(MbeansDescriptorsDigesterSource.class);
private static final Object dLock = new Object();
private Registry registry;
private final List<ObjectName> mbeans = new ArrayList<>();
private static Digester digester = null;
private static Digester createDigester() {
Digester digester = new Digester();
digester.setNamespaceAware(false);
digester.setValidating(false);
URL url = Registry.getRegistry(null, null).getClass().getResource
("/org/apache/tomcat/util/modeler/mbeans-descriptors.dtd");
digester.register
("-//Apache Software Foundation//DTD Model MBeans Configuration File",
url.toString());
// Configure the parsing rules
digester.addObjectCreate
("mbeans-descriptors/mbean",
"org.apache.tomcat.util.modeler.ManagedBean");
digester.addSetProperties
("mbeans-descriptors/mbean");
digester.addSetNext
("mbeans-descriptors/mbean",
"add",
"java.lang.Object");
digester.addObjectCreate
("mbeans-descriptors/mbean/attribute",
"org.apache.tomcat.util.modeler.AttributeInfo");
digester.addSetProperties
("mbeans-descriptors/mbean/attribute");
digester.addSetNext
("mbeans-descriptors/mbean/attribute",
"addAttribute",
"org.apache.tomcat.util.modeler.AttributeInfo");
digester.addObjectCreate
("mbeans-descriptors/mbean/notification",
"org.apache.tomcat.util.modeler.NotificationInfo");
digester.addSetProperties
("mbeans-descriptors/mbean/notification");
digester.addSetNext
("mbeans-descriptors/mbean/notification",
"addNotification",
"org.apache.tomcat.util.modeler.NotificationInfo");
digester.addObjectCreate
("mbeans-descriptors/mbean/notification/descriptor/field",
"org.apache.tomcat.util.modeler.FieldInfo");
digester.addSetProperties
("mbeans-descriptors/mbean/notification/descriptor/field");
digester.addSetNext
("mbeans-descriptors/mbean/notification/descriptor/field",
"addField",
"org.apache.tomcat.util.modeler.FieldInfo");
digester.addCallMethod
("mbeans-descriptors/mbean/notification/notification-type",
"addNotifType", 0);
digester.addObjectCreate
("mbeans-descriptors/mbean/operation",
"org.apache.tomcat.util.modeler.OperationInfo");
digester.addSetProperties
("mbeans-descriptors/mbean/operation");
digester.addSetNext
("mbeans-descriptors/mbean/operation",
"addOperation",
"org.apache.tomcat.util.modeler.OperationInfo");
digester.addObjectCreate
("mbeans-descriptors/mbean/operation/descriptor/field",
"org.apache.tomcat.util.modeler.FieldInfo");
digester.addSetProperties
("mbeans-descriptors/mbean/operation/descriptor/field");
digester.addSetNext
("mbeans-descriptors/mbean/operation/descriptor/field",
"addField",
"org.apache.tomcat.util.modeler.FieldInfo");
digester.addObjectCreate
("mbeans-descriptors/mbean/operation/parameter",
"org.apache.tomcat.util.modeler.ParameterInfo");
digester.addSetProperties
("mbeans-descriptors/mbean/operation/parameter");
digester.addSetNext
("mbeans-descriptors/mbean/operation/parameter",
"addParameter",
"org.apache.tomcat.util.modeler.ParameterInfo");
return digester;
}
public void setRegistry(Registry reg) {
this.registry=reg;
}
public void setSource( Object source ) {
this.source=source;
}
@Override
public List<ObjectName> loadDescriptors( Registry registry, String type,
Object source) throws Exception {
setRegistry(registry);
setSource(source);
execute();
return mbeans;
}
public void execute() throws Exception {
if (registry == null) {
registry = Registry.getRegistry(null, null);
}
InputStream stream = (InputStream) source;
List<ManagedBean> loadedMbeans = new ArrayList<>();
synchronized(dLock) {
if (digester == null) {
digester = createDigester();
}
// Process the input file to configure our registry
try {
// Push our registry object onto the stack
digester.push(loadedMbeans);
digester.parse(stream);
} catch (Exception e) {
log.error(sm.getString("modules.digesterParseError"), e);
throw e;
} finally {
digester.reset();
}
}
for (ManagedBean loadedMbean : loadedMbeans) {
registry.addManagedBean(loadedMbean);
}
}
}

View File

@@ -0,0 +1,392 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.modeler.modules;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.Map.Entry;
import javax.management.ObjectName;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.modeler.AttributeInfo;
import org.apache.tomcat.util.modeler.ManagedBean;
import org.apache.tomcat.util.modeler.OperationInfo;
import org.apache.tomcat.util.modeler.ParameterInfo;
import org.apache.tomcat.util.modeler.Registry;
public class MbeansDescriptorsIntrospectionSource extends ModelerSource
{
private static final Log log = LogFactory.getLog(MbeansDescriptorsIntrospectionSource.class);
private Registry registry;
private String type;
private final List<ObjectName> mbeans = new ArrayList<>();
public void setRegistry(Registry reg) {
this.registry=reg;
}
/**
* Used if a single component is loaded
*
* @param type The type
*/
public void setType( String type ) {
this.type=type;
}
public void setSource( Object source ) {
this.source=source;
}
@Override
public List<ObjectName> loadDescriptors(Registry registry, String type,
Object source) throws Exception {
setRegistry(registry);
setType(type);
setSource(source);
execute();
return mbeans;
}
public void execute() throws Exception {
if( registry==null ) registry=Registry.getRegistry(null, null);
try {
ManagedBean managed = createManagedBean(registry, null,
(Class<?>)source, type);
if( managed==null ) return;
managed.setName( type );
registry.addManagedBean(managed);
} catch( Exception ex ) {
log.error(sm.getString("modules.readDescriptorsError"), ex);
}
}
// ------------ Implementation for non-declared introspection classes
private static final Hashtable<String,String> specialMethods = new Hashtable<>();
static {
specialMethods.put( "preDeregister", "");
specialMethods.put( "postDeregister", "");
}
private static final Class<?>[] supportedTypes = new Class[] {
Boolean.class,
Boolean.TYPE,
Byte.class,
Byte.TYPE,
Character.class,
Character.TYPE,
Short.class,
Short.TYPE,
Integer.class,
Integer.TYPE,
Long.class,
Long.TYPE,
Float.class,
Float.TYPE,
Double.class,
Double.TYPE,
String.class,
String[].class,
BigDecimal.class,
BigInteger.class,
ObjectName.class,
Object[].class,
java.io.File.class,
};
/**
* Check if this class is one of the supported types.
* If the class is supported, returns true. Otherwise,
* returns false.
* @param ret The class to check
* @return boolean True if class is supported
*/
private boolean supportedType(Class<?> ret) {
for (int i = 0; i < supportedTypes.length; i++) {
if (ret == supportedTypes[i]) {
return true;
}
}
if (isBeanCompatible(ret)) {
return true;
}
return false;
}
/**
* Check if this class conforms to JavaBeans specifications.
* If the class is conformant, returns true.
*
* @param javaType The class to check
* @return boolean True if the class is compatible.
*/
private boolean isBeanCompatible(Class<?> javaType) {
// Must be a non-primitive and non array
if (javaType.isArray() || javaType.isPrimitive()) {
return false;
}
// Anything in the java or javax package that
// does not have a defined mapping is excluded.
if (javaType.getName().startsWith("java.") ||
javaType.getName().startsWith("javax.")) {
return false;
}
try {
javaType.getConstructor(new Class[]{});
} catch (java.lang.NoSuchMethodException e) {
return false;
}
// Make sure superclass is compatible
Class<?> superClass = javaType.getSuperclass();
if (superClass != null &&
superClass != java.lang.Object.class &&
superClass != java.lang.Exception.class &&
superClass != java.lang.Throwable.class) {
if (!isBeanCompatible(superClass)) {
return false;
}
}
return true;
}
/**
* Process the methods and extract 'attributes', methods, etc.
*
* @param realClass The class to process
* @param methods The methods to process
* @param attMap The attribute map (complete)
* @param getAttMap The readable attributes map
* @param setAttMap The settable attributes map
* @param invokeAttMap The invokable attributes map
*/
private void initMethods(Class<?> realClass, Method methods[], Hashtable<String,Method> attMap,
Hashtable<String,Method> getAttMap, Hashtable<String,Method> setAttMap,
Hashtable<String,Method> invokeAttMap) {
for (int j = 0; j < methods.length; ++j) {
String name = methods[j].getName();
if (Modifier.isStatic(methods[j].getModifiers())) {
continue;
}
if (!Modifier.isPublic(methods[j].getModifiers())) {
if (log.isDebugEnabled()) {
log.debug("Not public " + methods[j] );
}
continue;
}
if (methods[j].getDeclaringClass() == Object.class) {
continue;
}
Class<?> params[] = methods[j].getParameterTypes();
if (name.startsWith("get") && params.length==0) {
Class<?> ret = methods[j].getReturnType();
if (!supportedType(ret)) {
if (log.isDebugEnabled()) {
log.debug("Unsupported type " + methods[j]);
}
continue;
}
name=unCapitalize(name.substring(3));
getAttMap.put(name, methods[j]);
// just a marker, we don't use the value
attMap.put(name, methods[j]);
} else if(name.startsWith("is") && params.length == 0) {
Class<?> ret = methods[j].getReturnType();
if (Boolean.TYPE != ret) {
if (log.isDebugEnabled()) {
log.debug("Unsupported type " + methods[j] + " " + ret );
}
continue;
}
name = unCapitalize(name.substring(2));
getAttMap.put(name, methods[j]);
// just a marker, we don't use the value
attMap.put(name, methods[j]);
} else if (name.startsWith("set") && params.length == 1) {
if (!supportedType(params[0])) {
if (log.isDebugEnabled()) {
log.debug("Unsupported type " + methods[j] + " " + params[0]);
}
continue;
}
name = unCapitalize(name.substring(3));
setAttMap.put(name, methods[j]);
attMap.put(name, methods[j]);
} else {
if (params.length == 0) {
if (specialMethods.get(methods[j].getName()) != null) {
continue;
}
invokeAttMap.put(name, methods[j]);
} else {
boolean supported = true;
for (int i = 0; i < params.length; i++ ) {
if (!supportedType(params[i])) {
supported = false;
break;
}
}
if (supported) {
invokeAttMap.put( name, methods[j]);
}
}
}
}
}
/**
* XXX Find if the 'className' is the name of the MBean or
* the real class ( I suppose first )
* XXX Read (optional) descriptions from a .properties, generated
* from source
* XXX Deal with constructors
*
* @param registry The Bean registry (not used)
* @param domain The bean domain (not used)
* @param realClass The class to analyze
* @param type The bean type
* @return ManagedBean The create MBean
*/
public ManagedBean createManagedBean(Registry registry, String domain,
Class<?> realClass, String type)
{
ManagedBean mbean= new ManagedBean();
Method methods[]=null;
Hashtable<String,Method> attMap = new Hashtable<>();
// key: attribute val: getter method
Hashtable<String,Method> getAttMap = new Hashtable<>();
// key: attribute val: setter method
Hashtable<String,Method> setAttMap = new Hashtable<>();
// key: operation val: invoke method
Hashtable<String,Method> invokeAttMap = new Hashtable<>();
methods = realClass.getMethods();
initMethods(realClass, methods, attMap, getAttMap, setAttMap, invokeAttMap );
try {
Enumeration<String> en = attMap.keys();
while( en.hasMoreElements() ) {
String name = en.nextElement();
AttributeInfo ai=new AttributeInfo();
ai.setName( name );
Method gm = getAttMap.get(name);
if( gm!=null ) {
//ai.setGetMethodObj( gm );
ai.setGetMethod( gm.getName());
Class<?> t=gm.getReturnType();
if( t!=null )
ai.setType( t.getName() );
}
Method sm = setAttMap.get(name);
if( sm!=null ) {
//ai.setSetMethodObj(sm);
Class<?> t = sm.getParameterTypes()[0];
if( t!=null )
ai.setType( t.getName());
ai.setSetMethod( sm.getName());
}
ai.setDescription("Introspected attribute " + name);
if( log.isDebugEnabled()) log.debug("Introspected attribute " +
name + " " + gm + " " + sm);
if( gm==null )
ai.setReadable(false);
if( sm==null )
ai.setWriteable(false);
if( sm!=null || gm!=null )
mbean.addAttribute(ai);
}
// This map is populated by iterating the methods (which end up as
// values in the Map) and obtaining the key from the value. It is
// impossible for a key to be associated with a null value.
for (Entry<String,Method> entry : invokeAttMap.entrySet()) {
String name = entry.getKey();
Method m = entry.getValue();
OperationInfo op=new OperationInfo();
op.setName(name);
op.setReturnType(m.getReturnType().getName());
op.setDescription("Introspected operation " + name);
Class<?> parms[] = m.getParameterTypes();
for(int i=0; i<parms.length; i++ ) {
ParameterInfo pi=new ParameterInfo();
pi.setType(parms[i].getName());
pi.setName(("param" + i).intern());
pi.setDescription(("Introspected parameter param" + i).intern());
op.addParameter(pi);
}
mbean.addOperation(op);
}
if( log.isDebugEnabled())
log.debug("Setting name: " + type );
mbean.setName( type );
return mbean;
} catch( Exception ex ) {
ex.printStackTrace();
return null;
}
}
// -------------------- Utils --------------------
/**
* Converts the first character of the given
* String into lower-case.
*
* @param name The string to convert
* @return String
*/
private static String unCapitalize(String name) {
if (name == null || name.length() == 0) {
return name;
}
char chars[] = name.toCharArray();
chars[0] = Character.toLowerCase(chars[0]);
return new String(chars);
}
}

View File

@@ -0,0 +1,44 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.modeler.modules;
import java.util.List;
import javax.management.ObjectName;
import org.apache.tomcat.util.modeler.Registry;
import org.apache.tomcat.util.res.StringManager;
/**
* Source for descriptor data. More sources can be added.
*/
public abstract class ModelerSource {
protected static final StringManager sm = StringManager.getManager(Registry.class);
protected Object source;
/**
* Load data, returns a list of items.
*
* @param registry The registry
* @param type The bean registry type
* @param source Introspected object or some other source
* @return a list of object names
* @throws Exception Error loading descriptors
*/
public abstract List<ObjectName> loadDescriptors(Registry registry,
String type, Object source) throws Exception;
}

View File

@@ -0,0 +1,59 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<html>
<head>
<title>org.apache.commons.modeler.modules</title>
</head>
<body>
<p>Implementation classes - should not be used directly. The API is not stable
but eventually the code will be refactored as a collection of mbeans that will be useable
( more or less ) independently.</p>
<p>The MbeanDescriptors* classes are used to extract metadata from different sources. They
are result of few stages of refactoring - now they look very similar with ant tasks and are
close to normal mbeans, with an execute() method. DOM, SER, Introspection and Dynamic mbean
will load metadata from the corresponding sources.
</p>
<p>MbeansSource will load an extended MLET file, similar with jboss. It is not completely
implemented - only modeler mbeans and dynamic mbeans are loaded. The important characteristic
is that all declared mbeans will be registered in the mbean server as model mbeans. For
regular java classes, the description will be used to construct the model mbean. DynamicMbeans
metadata will be converted to model mbean and the model mbean wrapper will be loaded.</p>
<p>The goal of MbeansSource is to implement a simple persistence mechanism. Since all components
are model mbeans, we can detect all changes. The source will be loaded as DOM and modifications
will be made to the tree. The save() method will save the DOM tree - preserving all comments
and having only the changes that are needed.</p>
<p>There are few remaining issues. First, we need to use the persistence metadata to avoid
saving transient fields ( we save an attribute when we detect a change - but we don't know
if this attribute should be saved ). The solution is to use the persistence fields in the
spec - with some reasonable defaults or patterns for introspection or backward compat.
</p>
<p>Another problem is implementing adding and removing components. In catalina, a
factory is used to create the components, and save will operate on all mbeans.
For creation we need to also use a factory - using the "Type" as a parameter. This
will also work very well with Ant1.6 where we can use the component factory to
do a "natural" mapping ( i.e. mbeans can be treated as tasks, with attributes as
task attributes ). The second part can be solve by either using a parameter on
the factory method ( saveTo ? ), or by having a single mbeans source per domain.
</p>
</body>
</html>

View File

@@ -0,0 +1,248 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<html>
<head>
<title>Package Documentation for COMMONS-MODELER</title>
</head>
<body bgcolor="white">
<p>The <em>Modeler</em> component of the Commons project
offers convenient support for configuring and instantiating Model MBeans
(management beans), as described in the JMX Specification. It is typically
used within a server-based application that wants to expose management
features via JMX. See the
<a href="http://java.sun.com/products/JavaManagement/download.html">
JMX Specification (Version 1.1)</a> for more information about Model MBeans
and other JMX concepts.</p>
<p>Model MBeans are very powerful - and the JMX specification includes a
mechanism to use a standard JMX-provided base class to satisfy many of the
requirements, without having to create custom Model MBean implementation
classes yourself. However, one of the requirements in creating such a
Model MBean is to create the corresponding metadata information (i.e. an
implementation of the
<code>javax.management.modelmbean.ModelMBeanInfo</code> interface and its
corresponding subordinate interfaces). Creating this information can be
tedious and error prone. The <em>Modeler</em> package makes the process
much simpler, because the required information is constructed dynamically
from an easy-to-understand XML description of the metadata. Once you have
the metadata defined, and registered at runtime in the provided
<a href="Registry.html">Registry</a>, <em>Modeler</em> also supports
convenient factory methods to instantiate new Model MBean instances for you.
</p>
<p>The steps required to use Modeler in your server-based application are
described in detail below. You can find some simple usage code in the unit
tests that come with Modeler (in the <code>src/test</code> subdirectory of the
source distribution), and much more complex usage code in Tomcat 4.1 (in the
<code>org.apache.catalina.mbeans</code> package).</p>. More advanced uses can
be found in Tomcat 5.
<h2>1. Acquire a JMX Implementation</h2>
<p><em>Modeler</em> has been tested with different JMX implementations:
<ul>
<li>JMX Reference Implementation (version 1.0.1 or later) -
<a href="http://java.sun.com/products/JavaManagement/download.html">
http://java.sun.com/products/JavaManagement/download.html</a></li>
<li>MX4J (version 1.1 or later) -
<a href="http://mx4j.sourceforge.net/">http://mx4j.sourceforge.net</a></li>
<li>JBoss MX
<a href="http://www.jboss.org/">http://www.jboss.org</a></li>
</ul>
<p>After unpacking the release, you will need to ensure that the appropriate
JAR file (<code>jmxri.jar</code> or <code>mx4j.jar</code>) is included on your
compilation classpath, and in the classpath of your server application when it
is executed.</p>
<h2>2. Create a Modeler Configuration File</h2>
<p><em>Modeler</em> requires that you construct a configuration file that
describes the metadata ultimately need to construct the
<code>javax.management.modelmbean.ModelMBeanInfo</code> structure that is
required by JMX. Your XML file must conform to the
<a href="../../../../../../mbeans-descriptors.dtd">mbeans-descriptors.dtd</a>
DTD that defines the acceptable structure.</p>
<p>Fundamentally, you will be constructing an <code>&lt;mbean&gt;</code>
element for each type of Model MBean that a registry will know how to create.
Nested within this element will be other elements describing the constructors,
attributes, operations, and notifications associated with this MBean. See
the comments in the DTD for detailed information about the valid attributes
and their meanings.</p>
<p>A simple example configuration file might include the following components
(abstracted from the real definitions found in Tomcat 4.1's use of Modeler):
</p>
<pre>
&lt;?xml version="1.0"?&gt;
&lt;!DOCTYPE mbeans-descriptors PUBLIC
"-//Apache Software Foundation//DTD Model MBeans Configuration File"
"http://jakarta.apache.org/commons/dtds/mbeans-descriptors.dtd"&gt;
&lt;mbeans-descriptors&gt;
&lt;!-- ... other MBean definitions ... --&gt;
&lt;mbean name="Group"
className="org.apache.catalina.mbeans.GroupMBean"
description="Group from a user database"
domain="Users"
group="Group"
type="org.apache.catalina.Group"&gt;
&lt;attribute name="description"
description="Description of this group"
type="java.lang.String"/&gt;
&lt;attribute name="groupname"
description="Group name of this group"
type="java.lang.String"/&gt;
&lt;attribute name="roles"
description="MBean Names of roles for this group"
type="java.lang.String[]"
writeable="false"/&gt;
&lt;attribute name="users"
description="MBean Names of user members of this group"
type="java.lang.String[]"
writeable="false"/&gt;
&lt;operation name="addRole"
description="Add a new authorized role for this group"
impact="ACTION"
returnType="void"&gt;
&lt;parameter name="role"
description="Role to be added"
type="java.lang.String"/&gt;
&lt;/operation&gt;
&lt;operation name="removeRole"
description="Remove an old authorized role for this group"
impact="ACTION"
returnType="void"&gt;
&lt;parameter name="role"
description="Role to be removed"
type="java.lang.String"/&gt;
&lt;/operation&gt;
&lt;operation name="removeRoles"
description="Remove all authorized roles for this group"
impact="ACTION"
returnType="void"&gt;
&lt;/operation&gt;
&lt;/mbean&gt;
&lt;!-- ... other MBean definitions ... --&gt;
&lt;/mbeans-descriptors&gt;
</pre>
<p>This MBean represents an instance of <em>org.apache.catalina.Group</em>,
which is an entity representing a group of users (with a shared set of security
roles that all users in the group inherit) in a user database. This MBean
advertises support for four attributes (description, groupname, roles, and
users) that roughly correspond to JavaBean properties. By default, attributes
are assumed to have read/write access. For this particular MBean, the roles
and users attributes are read-only (<code>writeable="false"</code>). Finally,
this MBean supports three operations (addRole, removeRole, and
removeRoles) that roughly correspond to JavaBean methods on the underlying
component.</p>
<p>In general, <em>Modeler</em> provides a standard ModelMBean implementation
that simply passes on JMX calls on attributes and operations directly through
to the managed component that the ModelMBean is associated with. For special
case requirements, you can define a subclass of
<a href="BaseModelMBean.html">BaseModelMBean</a> that provides override
methods for one or more of these attributes (i.e. the property getter and/or
setter methods) and operations (i.e. direct method calls).
<p>For this particular MBean, a custom BaseModelMBean implementation subclass
is described (<code>org.apache.catalina.mbeans.GroupMBean</code>) is
configured. It was necessary in this particular case because several of the
underlying Catalina component's methods deal with internal objects or arrays of
objects, rather than just the Strings and primitives that are supported by all
JMX clients. Thus, the following method on the <code>Group</code> interface:
</p>
<pre>
public void addRole(Role role);
</pre>
<p>is represented, in the MBean, by an <code>addRole</code> method that takes
a String argument representing the role name of the required role. The MBean's
implementation class acts as an adapter, and looks up the required Role
object (by name) before calling the <code>addRole</code> method on the
underlying <code>Group</code> instance within the Server.</p>
<h2>3. Create Modeler Registry at Startup Time</h2>
<p>The metadata information, and the corresponding Model MBean factory, is
represented at runtime in an instance of <a href="Registry.html">Registry</a>
whose contents are initialized from the configuration file prepared as was
described above. Typically, such a file will be included in the JAR file
containing the MBean implementation classes themselves, and loaded as follows:
</p>
<pre>
URL url= this.getClass().getResource
("/com/mycompany/mypackage/mbeans-descriptors.xml");
Registry registry = Registry.getRegistry();
registry.loadMetadata(url);
</pre>
<p>Besides using the configuration file, it is possible to configure the
registry metadata by hand, using the <code>addManagedBean()</code> and
<code>removeManagedBean()</code> methods. However, most users will find
the standard support for loading a configuration file to be convenient
and sufficient.</p>
<p>Modeler will also look for an mbeans-descriptors.xml in the same package
with the class being registered and in its parent. If no metadata is found,
modeler will use a number of simple patterns, similar with the ones used by
ant, to determine a reasonable metadata</p>
<p>In a future version we should also support xdoclet-based generation of the
descriptors</p>
<h2>4. Instantiate Model MBeans As Needed</h2>
<p>When your server application needs to instantiate a new MBean and register
it with the corresponding <code>MBeanServer</code>, it can execute code like
this:</p>
<pre>
Group group = ... managed component instance ...;
MBeanServer mserver = registry.getMBeanServer();
String oname="myDomain:type=Group,name=myGroup";
registry.registerComponent( group, oname, "Group" );
</pre>
<p>After the Model MBean has been created and registered, it is accessible to
JMX clients through the standard JMX client APIs.
</p>
</body>
</html>