AMX: Implementation

by Lloyd L Chambers
Last updated: 11 April 2006

Contents

1. Introduction
  1.1 Package names
  1.2 Delegation
  1.3 Implementation class hierarchy
  1.4 How AMX MBeans are loaded
  1.5 Dynamic Client Proxies
  1.6 Utility MBeans (and their AMX interfaces)
  1.7 Utility and convenience classes

1. Introduction and overview

This document provides an overview of the Glassfish AppServer Management Extensions (AMX) management API implementation in AppServer 9.0 . It assumes you have already read and understood AMX: Design and Use.

You should read section 1 and its subsections before proceeding to the later sections.

1.1 Package names

There are two top-level Java package names in AMX. All other packages are subpackages thereof:

Prefix
Glassfish module
Comments
com.sun.appserv.management

admin-core/mbeanapi

directories src and tests

Public interfaces and client support code
com.sun.enterprise.management admin/mbeanapi-impl Implementation files

For brevity in this document, class names might not use fully qualified package names . Implementation classes might drop the "com.sun.enterprise.management" portion, and interface classes might drop the "com.sun.appserv.management" portion.

For example, the following two class names are intended to be the same:
offline.ConfigDelegate
com.sun.enterprise.management.offline.ConfigDelegate

The context should usually be clear.

1.2. Delegation

Delegation is heavily used in AMX for the configuration, monitoring and JSR 77 MBeans, though future changes may ultimately remove most of the delegation now used, by moving implementation code directly into the AMX MBeans themselves (or supporting objects used by the AMX MBeans).

From the perspective of an AMX MBean, the only interface involved for delegation is support.Delegate. Since AMX MBeans must support a JMX-style interface, they in turn can be completely implemented against a Delegate that supports the same type of facilities:

public interface Delegate
{
public Object getAttribute( String name )throws AttributeNotFoundException;
public boolean supportsAttribute( String name );
public AttributeList getAttributes( final String[] attrNames );
public void setAttribute( final Attribute attrName
throws AttributeNotFoundException, InvalidAttributeValueException;
public AttributeList setAttributes( final AttributeList attrs );
public boolean supportsOperation(String name,
Object[] args, String[] types );
public MBeanInfo getMBeanInfo();
public Object invoke( String operationName,
Object[] args, String[] types );
public void setOwner( DelegateOwner owner );
}

The class support.DelegateToMBeanDelegate implements Delegate by calling another MBean, specifically one of the MBeans found in the JMX domain com.sun.appserv. For example the AMX MBean amx:j2eeType=X-DomainConfig,name=na delegates to the MBean com.sun.appserv:type=domain,category=config.

AMX attributes are all consistently named, including the use of "camel case" and all-uppercase acronyms. Part of the role of the Delegate is to map the AMX Attribute name ("LogRoot") to the name within the com.sun.appserv Delegate ("log-root"). In some cases, this mapping is explicit, but in most cases an automatic algorithm is sufficient; this is implemented by AMXAttributeNameMapper[Impl].

AMX operations are also handled by the Delegate via supportsOperation() and invoke(). However, there are few operations in AMX to begin with, and even fewer that can be directly handled by the Delegate, because AMX uses an object-oriented model, whereas the com.sun.appserv MBeans tend to use a monolithic, function-oriented approach. For example, a com.sun.appserv MBean might include a "target name" (such as a server name) as one of the parameters, whereas in AMX the target is implicit, since every element exists as an appropriate MBean (eg a StandaloneServerConfig).

There is an "offline config" feature within AMX (not yet exposed) which makes use of another implementation of Delegate: offline.ConfigDelegate, which implements Delegate using the low-level Config API (com.sun.enterprise.config.ConfigContext, et al). This implementation will be the basis for a future conversion of AMX in which the com.sun.appserv MBeans are no longer used for configuration.

The beauty of the Delegate approach is that AMX MBeans require no modification whatsoever, whether they're running using DelegateToMBeanDelegate or ConfigDelegate (excepting creation and deletion of configuration elements, because the underlying APIs diverge).

1.3 Implementation class hierarchy

The implementation class hierarchy has a close correspondence to the interface hierarchy. The following table shows some common base interfaces and their corresponding base implementation classes, from which many AMX MBeans extend:

Interface Implementation
AMX, AMXDebug, Container AMXImplBase (extended by AMXNonConfigImplBase)
AMXConfig AMXConfigImplBase
ConfigFactoryCallback (impl) ConfigFactory, ResourceFactoryImplBase
J2EEManagedObject J2EEManagedObjectImplBase
Monitoring MonitoringImplBase
MonitoringStats MonitoringStatsImplBase (extends MonitoringImplBase)
BeanMonitor BeanMonitorImplBase (extends MonitoringStatsImplBase)
ModuleConfig DeployedItemConfigBase (extends AMXConfigImplBase)
J2EEResource J2EEResourceImplBase
EJB EJBImplBase
J2EEModule J2EEModuleImplBase
AMXJMXMonitor JMXMonitorBase

Every AMX MBean implementation class follows a consistent naming pattern: the implementation class name is <interface-name>Impl. Here are just a few examples:

Interface Implementation
DomainRoot DomainRootImpl
   
DomainConfig DomainConfigImpl
HTTPListenerConfig HTTPListenerConfigImpl
   
ServerRootMonitor ServerRootMonitorImpl
ServletMonitor ServletMonitorImpl
   
J2EEDomain J2EEDomainImpl
J2EEServer J2EEServerImpl
   
BulkAccess BulkAccessImpl
DeploymentMgr DeploymentMgrImpl

In general, implementations almost always use "Impl" as a suffix, even in internal implementation code. For example a commony used suffix is "ConfigFactory"; there are many classes which extend the base ConfigFactory class (not an interface in that case, but an actual base class).

1.4 How AMX MBeans are loaded

AMX MBeans area loaded by support.Loader, implementing LoaderMBean. There are two implementations of LoaderMBean which both extend LoaderBase, support.Loader, designed to load MBeans in response to the registration and deregistration of com.sun.appserv MBeans, and offline.OfflineLoader, designed to load AMX MBeans (mainly AMXConfig MBeans) in "offline mode" (server not running).

1.4.1 com.sun.enterprise.management.support.Loader details

This loader is called by the internal startup code not long into the server startup process. It itself is an MBean, implementing LoaderMBean, and runs for the life of the server. When it is instantiated, it creates a LoaderRegThread, which handles registration and deregistration of AMX MBeans asynchronously. It also starts a DeferredRegistrationThread, which is used when registration of AMX MBeans must be deferred (due to out-of-order registration of com.sun.appserv MBeans).

When Loader.startHook() is called, all appropriate currently-registered com.sun.appserv MBeans are queued with the LoaderRegThread, which asynchronously registers appropriate corresponding AMX MBeans. This phase can take a number of seconds, as the rest of the server is also starting. In fact, it might complete after the server says it has started (in the log), thus the need for DomainRoot.getAMXReady().

Following its startup phase, in which it queues existing com.sun.appserv MBeans for processing with the LoaderRegThread, it then tracks (via JMX Notifications from the MBeanServerDelegateMBean) the registration or deregistration of com.sun.appserv MBeans which are used by AMX MBeans as Delegates. When a com.sun.appserv MBean is registered, Loader instantiates a DelegateToMBeanDelegate, then instantiates the appropriate AMX MBean, passing the Delegate instance to its constructor. The pair is inserted into an internal table which maps the com.sun.appserv ObjectName to the corresponding AMX ObjectName. When a com.sun.appserv MBean is deregistered, the corresponding AMX MBean (if any) is deregistered also by virtue of this table.

Subsequent activity is minimal, occuring only when MBeans need to come or go, such as when monitoring is enabled or disabled, when configuration is created or deleted, or when a server starts or stops.

Please note that there is a hack in Loader.handleNotification() [search for WORKAROUND_FOR_BUG_SRIDATTA_FOUND] which works around a problem in some internal server code by forcing synchronous loading when MBeanServerNotifications are received. This problem might or might not still exist; retesting should be done at some point, and the workaround removed if possible.

Deferred registration is necessary in cases when an MBean which logically contains another MBean has not yet been registered. The AMX hierarchy ostensibly maintains an invariant which requires that the Container for any AMX MBean must be registered prior to registration of any Containees. In practice, this invariant is difficult to enforce, and at times does not hold because there are certain flaws in some subsystems (such as the web server monitoring MBeans) which omit registration for one or more system apps. Nevertheless, the invariant holds for 99+% of the registered MBeans.

Deferred registration is handled by DeferredRegistrationThread, which checks periodically if conditions are suitable. A typical scenario in which registration is deferred occurs when a remote server starts up; its MBeans are cascaded into the DAS in arbitrary order. Usually a few seconds is sufficient for all out-of-order deferred registrations to complete; in some cases there appear to be cascading problems that miss one or more MBeans; this can result in periodic (and benign) log messages from DeferredRegistrationThread.

1.4.2 com.sun.enterprise.management.offline.OfflineLoader details

This loader is called by a client to load MBeans into a specified in-process MBeanServer; the client passes the desired MBeanServer and java.io.File specifying domain.xml.

The MBeans are loaded synchronously by calling offline.AMXLoader, which traverses domain.xml via the low-level Config API. Because config elements cannot (yet) be created or deleted using offline.ConfigDelegate, the resulting registered MBeans do not come and go; no threads are created.

A useful enhancement (and one necessary to divorce AMXConfig MBeans from com.sun.appserv:category=config MBeans) would be to implement the createAbc() and removeAbc() methods [eg DomainConfig.createStandaloneServerConfig()] using the low-level Config API. Because there are no com.sun.appserv MBeans loaded in "offline" mode (these exist only in a running server), the various create/remote operations in AMX MBeans will fail if called; their Delegates are lacking the required methods to create or remove configuration elements.

1.5 Dynamic Client Proxies

A client using AMX may use JMX via MBeanServerConnection. This may well be the most useful approach for more generic applications, but is none too friendly for other uses. AMX solves this problem by making use of java.lang.reflect.Proxy.

Each AMX interface can be used via a corresponding Dynamic Client Proxy (DCP), which implements the AMX interface by storing the ObjectName and MBeanServerConnection of the target MBean and generically implementing all of the Attributes and operations defined by the corresponding AMX interface.

The key class involved is client.handler.AMXProxyHandler. Among other things, it deduces request for Attribute(s), calling MBeanServerConnection.get/setAttribute(s) appropriately. Operations are handled in a similar manner.

A key function of AMXProxyHandler is converting return values of type Map<String,ObjectName> or Set<ObjectName> into <T extends AMX> Map<String,T> and <T extends AMX>Set<AMX>, which are the types "seen" in the AMX interfaces. This is done as follows:

  1. For any invoke() with a method name of the form getAbcMap() or getAbcSet(), call the corresponding getAbcObjectNameMap() or getAbcObjectNameSet().
  2. Take the returned Map<String,ObjectName> or Set<String,ObjectName> and call ProxyFactory.toProxyMap(), ProxyFactory.toProxySet() or ProxyFactory.toProxyList() as appropriate.
  3. Return the resulting Map or Set.

For example, DomainConfigImpl has an operation getClusterConfigObjectNameMap() returning Map<String,ObjectName>, which AMXProxyHandler returns to the client as Map<String,ClusterConfig>. Each ClusterConfig in the resulting Map is a Dynamic Client Proxy with an embedded MBeanServerConnection and ObjectName.

The client.handler.AMXProxyHandler class is extended by the base class ConverterHandler. The ConverterHandler subclasses exist in order to convert Map<String,Serializable> (the across-the-wire form for complex types) into appropriate implementations of AMX interfaces, such as WebServiceEndpointInfo, LogQueryResult, and a few others. The ProxyFactory class is responsible for creating the appropriate Dynamic Client Proxy instance, which could be AMXProxyHandler itself, or one of its ConverterHandler subclasses (created indirectly by ConverterHandlerFactory).

1.5.1 Caching in Dynamic Client Proxies

Certain items are cached within an AMX Dynamic Client Proxy.

In particular, MBeanInfo is cached, provided that the Attribute MBeanInfoIsInvariant is true. Nearly all AMX MBeans do in fact have invariant MBeanInfo, and so it is cached within the DCP. This is a good thing, since it is a relatively expensive operation not just to create the MBeanInfo, but to serialize it and pass it back over the wire. Exceptions to this invariant include ConfigDottedNames and MonitoringDottedNames, which refresh their MBeanInfo periodically (or upon request); the MBeanInfo consists of a large number of Attributes which depend upon the configuration and monitoring state, both of which can change dynamically.

Besides MBeanInfo, certain other Attributes are cached which are known to be invariant. This includes ContainerObjectName, InterfaceName, FullType, Group and Name.

1.6 Utility MBeans (and their AMX interfaces)

Utility MBeans implement the marker interface Utility, and include BulkAccess, QueryMgr, NotificationServiceMgr, JMXMonitorMgr,Sample, DeploymentMgr, and UploadDownloadMgr. Such MBeans are Singletons; only one instance of them ever exists within the Domain Admin Server.

Additionally, there is SystemInfo, though it does not extend Utility and Singleton due to an oversight.

1.7 Utility and convenience classes

AMX implementation classes make very heavy use of utility methods—the author believing that any generally applicable method needed more than once should be coded and tested once, then reused everywhere it is needed, rather than the oft-seen sprinkling of repeated inline code. Because they are needed both on the server side and in client-side implementation code, they are part of the client API, and are thus available to implementors as well as being used internally.

Utility and convenience classes can be divided into two major categories:

  • general-purpose utilities which are applicable for use in any software;
  • more task-specific utilities, which frequently build upon the general-purpose ones.

General-purpose utilities are all (and always) found in one of the subpackages of com.sun.appserv.management.util. These packages include:

Subpackage Comments
util.jmx
Numerous JMX-related classes and methods. Of particular note is JMXUtil, containing methods such as:

See the package javadoc for details.

util.jmx.stringifier

Stringifier classes specific to JMX classes.

See util.stringifier below.

util.misc

This is the most heavily used utility package because it contains classes for working with the various java.util.Collection classes, which are heavily used in AMX. Classes of note include:

util.stringifier

The Stringifier classes provide the ability to "stringify" any object intelligently.

Stringifiers are more useful and flexible than toString()because they can be written for any object, thus providing missing or improved functionality, or an alternate or customizable renderings of an Object into a String. For example, arrays are recursively stringified, which is much more useful than a hexadecimal object-address.

util.j2ee
util.j2ee.stringifier

J2EEUtil, contains methods mainly for use with JSR 77 MBeans.

The Stringifier classes are useful for getting a more readable String version of Stats and Statistics.

 

2. Adding a new AMX MBean

...to be continued...

2.X. AMX Unit tests

AMX development was and is greatly aided by these unit tests, which detect many different implementation flaws. They are easy to run, and it's easy to add new tests.

Please see glassfish/admin/mbeanapi-impl/tests/amx-unit-tests.html for details (you will need to check out the Glassfish code using cvs).