Adding AMX MBeans
Author: Lloyd L Chambers

AMX Internal Developer's Guide
Last updated: 20 July 2005 18:01 PST
Author(s): lloyd.chambers@sun.com
0. Checkin requirements
Checking in or changing the AMX APIs is an activity that must be approved prior to checkin by amx-dev@sun.com.
This means almost everything in com.sun.appserv.management and all its subpackages. The AMX APIs are the publicly supported APIs for managing the appserver and require much more stringent review than interfaces used elsewhere. They should see very infrequent change, and must be consistent in multiple ways with the other interfaces already in AMX.
An AMX interface should generally not be checked in until it is fully-javadoc'd and in its final form. Any AMX interface which is subject to change must be clearly javadoc'd as such as follows. The following javadoc entry in the source code should be used:
<b>This interface has not been finalized and is subject to change. Except by special arrangement, it is informational only and should not be used.</b>
It is possible to introduce new interfaces without providing any implementation. In this case, the amx-dev code review and approval is the critical step that must be followed. The unit tests must still be run as a sanity check prior to checkin.
Pre checkin checklist:
___ 1. Added new unit test(s) to amxtest.classes or modified existing ones appropriately. Please note that unit tests apply to not only AMX MBeans, but code in com.sun.appserv.management.util.* (although many are not currently tested).
___ 2. Ran the unit tests with amxtest.classes.
___ 3. Verified that all tests pass excepting known issues.
___ 4. Code reviewed and approved by amx-dev@sun.com. It is especially important that AMX interfaces (APIs) be reviewed prior to checkin. Please follow this rule strictly.
1. Adding AMX MBeans
This is a brief summary of how to add an MBean to the AMX MBean API.
1.1. Add a type for your new MBean in XTypes.java (or J2EETypes.java if it's a JSR 77 extension). Be sure to follow the javadoc and naming conventions. Example:
/**
ID for {@link com.sun.appserv.management.base.LogRecordEmitter}
*/
public final static String LOG_RECORD_EMITTER = P + "LogRecordEmitter";
1.2. Define the interface for your MBean. Your interface should extend com.sun.appserv.management.base.AMX, or one of its sub-interfaces. The static variable J2EE_TYPE should be defines as the type you added in step (1). All interface additions and/or changes to AMX must be approved prior to checkin by the AMX team. Be sure to read the AMX Conventions.
public interface LogRecordEmitter
extends AMX
{
/** The j2eeType as returned by {@link com.sun.appserv.management.base.AMX#getJ2EEType}. */
public static final String J2EE_TYPE = XTypes.LOG_RECORD_EMITTER;
}
Notes:

- All AMX MBeans extend javax.management.NotificationEmitter, so there is no need to extend it in your interface.

1.3. Add an entry for your interface in the MBEANINTERFACES Class[] in XTypesMapper (or J2EETypesMapper if it's a JSR 77 extension). Example:
private static final Class[] MBEANINTERFACES =
{
...
LogRecordEmitter.class
...
};
1.4. Define containment within the AMX hierarchy by adding an appropriate entry in the com.sun.enterprise.management.support.TypeInfos.TypeData array. Examples:
new DomainRootChild( XTypes.MONITORING_ROOT ),
new MiscChild( XTypes.NOTIFICATION_SERVICE, XTypes.NOTIFICATION_SERVICE_MGR),
1.5. Decide what ObjectName your MBean will have.
For config or monitoring MBeans, this is generally taken care of by the AMX infrastructure code.
1.5.1 Config mbeans
If it's a new config type, you'll need to add something to com.sun.enterprise.management.support.OldConfigTypes.
1.5.2 Monitoring mbeans
If it's a new monitoring type, you'll probably need to add something to com.sun.enterprise.management.support.OldMonitorTypes.
1.5.3 Other mbeans
You will probably need to define the ObjectName yourself. The minimum ObjectName should look like:
amx:j2eeType=type,name=name
By default only the above form is used. This is sufficient if the MBean is a Singleton or Utility .
If the MBean is contained within another MBean, additional properties may be needed for the containment hierarchy. However, in most cases, this is handled as part of the ObjectName implied by the containment hierarchy.
1.6. Implementing your MBean.
The MBean should extend com.sun.enterprise.management.support.AMXImplBase or one of its subclasses. Possible subclasses include:

- com.sun.enterprise.management.support.AMXNonConfigImplBase
- com.sun.enterprise.management.support.AMXConfigImplBase
- com.sun.enterprise.management.monitor.MonitoringImplBase
- com.sun.enterprise.management.monitor.MonitoringStatsImplBase
- com.sun.enterprise.management.j2ee.J2EEManagedObjectImplBase

In the course of implementing be particularly aware of methods with the following classes; in general if you think you need to do something with AMX, there's probably already a routine to do it:

- Container.
- AMX.
- Util.
- QueryMgr.
- the large selection of utilities within com.sun.appserv.management.util.
- inherited method of many kinds, including getMBeanServer(), getJMXDomain(), getObjectName(), logXXX(), getContainer(), getDomainRoot(), getJ2EEType().

If your MBean emits Notifications, there are things to be aware of:

- NotificationBuilder
- inherited methods getNotificationBuilder(), getNotificationEmitter(), sendNotification(), createNotificationBuilder().
- Be sure to define all Notification types that your MBean may emit. See DeploymentMgr for examples. Note that all types should have the suffix NOTIFICATION_TYPE.
- All emitted Notifcations should follow the AMX conventions of placing extra data in a Map found in Notification.getUserData(). NotificationBuilder helps with this. Be sure to define keys (constants) in your interface for each piece of data.

If you issue Notifications of type javax.management.Notification, the following are sufficient for the AMX infrastructure to properly expose the MBeanNotificationInfo[] required by JMX:
private final String[] NOTIF_TYPES = new String[]
{
... all types emitted, see DeploymentMgrImpl for an example...
};
protected Set
getNotificationTypes( Set existing )
{
return SetUtil.newSet( existing, SetUtil.newSet( NOTIF_TYPES ) );
}
Avoid importing internal appserver classes. This is very important for speeding development and reducing code coupling, compile time, compile-order dependencies, etc. Whenever possible, get the information you need by calling another MBean (preferably AMX). Any MBean has available to it all the client-side facilities that any client does. Your MBean inherites the methods getProxy(), getProxyFactory(), getNamedChildProxy(). You can get a proxy to any AMX MBean by calling getProxyFactory().getProxy( objectName ). Be aware, also, of getOldConfigProxies() if you need to access com.sun.appserv MBeans.
Actively fail unimplemented operations. If you haven't implemented some operations, be sure to code them using a call to unimplementedOperation(), which is inherited from AMXImplBase:
public int myUnimplementedOperation() {
unimplementedOperation( "myUnimplementedOperation" );
}
Please also be aware of unimplementedAttribute() and impossible().rv MBeans.
Use the correct package name. MBean implementations should be placed in the appropriate subpackage of com.sun.enterprise.management, in the glassfish/admin/mbeanapi-impl module. Never place implementation code in com.sun.appserv.management or its subpackages--except for certain client support code and utility code, com.sun.appserv.management is strictly public interfaces.
1.7. Ensuring your MBean gets loaded
There are various ways AMX MBeans get registered or unregistered.
Most AMX MBeans, such as the config or monitoring MBeans are driven entirely by the registration or unregistration of their corresponding com.sun.appserv MBeans. This process is coordinated by com.sun.enterprise.management.support.Loader.
Only a very few AMX MBeans are loaded explicity--examples of this include DomainRoot and SystemInfo. The containment hierarchy causes Singleton and Utility MBeans to load implicitly. For example, the QueryMgr is loaded because it is a Utility MBean; when DomainRoot is loaded this causes the QueryMgr to also be loaded, because it is contained within DomainRoot. See AMXImplBase.registerSelfMgrChildren() if this is of interest. Defining containment in step 4 above is sufficient for these cases to cause the appropriate MBean to be loaded.
As AMX evolves, there may be more cases where explicit loading is done. This appears to be the case, for example, with the WebServiceInfo MBeans. Ask Lloyd Chambers or Kedar Mhaswade if you're unsure.
1.8 Testing your AMX MBean
Please see these two guides on running and creating unit tests:
admin/mbeanapi-impl/tests/com/sun/enterprise/management/unit-tests-README.html
admin/mbeanapi-impl/tests/com/sun/enterprise/management/AddingAMXUnitTests.html
1.9 Acronyms and special cases for Attribute names
Special handing is required for acronyms. AMX has the convention that acronyms are always upper-case. This makes automatic mapping a problem in some cases. Also, there are a few cases where the AMX name does not match the underlying name in domain.xml.
For creation of any type of Config, the correct way to map the parameters is to add `getParamNameOverrides()` to the Factory. See any of the AbcFactory classes in the package com.sun.enterprise.management.config.
`protected Map<String,String>
getParamNameOverrides()
{
return( MapUtil.newMap( REFERENCED_CONFIG_NAME, "config-ref" ) );
}`
For the Attribute itself as fetched by getAttribute() in the registered and already-existing MBean, add the following method to the MBean implementation class (AbcImpl for the interface Abc). See any of the AbcImpl classes in the package com.sun.enterprise.management.config.
protected void
addCustomMappings( final AttributeNameMapper mapper ) {
mapper.addMapping( "autodeploy-jsp-precompilation-enabled", "AutodeployJSPPrecompilationEnabled" );
mapper.addMapping( "deploy-xml-validation", "DeployXMLValidation" );
}
It would be nice to reconcile these two, but that's the way it is for now.
2. AMX Conventions
The conventions used in the AMX API are very, very important for several reasons:

- the APIs are publicly documented. All customers wanting to access the AppServer programmatically are using these APIs. They need to be consistent, well-documented and unambiguous.
- the APIs cannot be easily changed and/or cannot be changed at all in minor releases.
- certain naming idioms are used by the internal implementation code

This table summarizes the conventions used in AMX:

Convention | Discussion
Suffixes Config, Monitor, Mgr | Any interface whose name ends with the suffix Config is a configuration MBean Any interface whose name ends with the suffix Monitor is a monitoring MBean Any interface whose name ends with the suffix Mgr is an MBean that has some sort of managerial control over operations or other MBeans Any MBean which is persistent configuration must have a class name that ends in "Config" and an XType (XTypes.java) that ends in "_CONFIG". Any MBean which is monitoring must have a class name that ends in "Monitor" and an XType that ends in "_MONITOR". The XType and class name should match ed "AbcMgr" and "ABC_MGR".
getAbcMap getAbcObjectNameMap | This is a special idiom. MBean implementations can only return ObjectName; they cannot return dynamic proxies that will work correctly. So the client side dynamic proxy code looks for the special suffix "ObjectNameMap". When found, it assumes that the Map values (not keys) are all ObjectName and instantiates dynamic proxies for each of them. The actual Map the client receives is a new map keyed by the same keys, but with values that are dynamic proxy implementations of the appropriate AMX interface for the particular ObjectName.
acronyms | All acronyms must be upper-case. This includes class names and method names.
utility routines | Do not add utility routines to MBeans. In almost all cases they are more suitably implemented in a class within the com.sun.appserv.management.helper package. If in doubt, ask first.
data types | No proprietary data types are to be used in any AMX interface. When a complex object is needed, the AMX convention is to use java.util.Map in most cases, or javax.management.openmbeans.CompositeData in certain cases. The only legal data types that may be used are those found in: com.sun.appserv.management.* java.util.* javax.management.* No other data types may be used.
method names | Do not use method names that are so obtuse they require javadoc to explain. Choose them very carefully; they are semi-permanent.
incompatible changes | By default, no incompatible changes are allowed. Such changes may only occur at a major release, and require approval by the AMX team and notification to customers.
javadoc | All methods and constants must be javadoc'd.
values of constants | Values of constants in AMX are always camel case eg: public static final String WEIGHTED_ROUND_ROBIN = "WeightedRoundRobin".

This table summarizes data type conventions used in AMX:

Convention | Discussion
method signatures and primitive types | Primitive types must always be used eg char, int, long, not Character, Integer, Long. The only time the Object form is acceptable is to indicate that the parameter is optional and therefore may be null.
return types | Return types should be restricted to java.lang types whenever possible. When more complex data is to be returned, a strongly-typed java.util.Map may be used eg Map<String,Integer>. In certain cases the JMX javax.management.openmbeans.CompositeData may be used.
no proprietary types | No proprietary types should be used for method signatures or return types. A client lacking the AMX client interfaces should be able to fully access all operations and Attributes with no fear of getting ClassNotFoundException, or worrying about serialization versions.