This document describes best practices that have been identified for modeling using the JMX API. The suggestions here are not carved in stone, but if you find that they are inappropriate for your modeling effort, please let us know so that we can discuss your situation and if necessary update this document. Some of the conventions described here are inconsistent with parts of the JMX API or of JSR 77 (J2EE management). They represent lessons learnt since those APIs were defined. Although the APIs cannot be changed to conform to the conventions here, these conventions should probably be followed in future additions to the APIs.

Contents

Object Names

Every JMX MBean has an Object Name. It is very important to choose consistent and usable Object Names as described here. Bear in mind that the client interacting with your model might be showing these Object Names directly to a human (for example, it is a JMX-aware console); or it might be a program that is accessing MBeans in your model whose names you have clearly defined. Also, of course, MBeans should be named consistently across the model and, as far as possible, between models.

Object Name Syntax

An Object Name is an instance of javax.management.ObjectName .

An Object Name is either the name of an MBean, or it is a pattern, potentially matching the names of many MBeans.

It looks like this:

domain: key-property-list

For example:

com.sun.someapp:type=Whatsit,name=25

Here, domain is an arbitrary string.

If the domain is empty, it implies the default domain for the MBean Server where the Object Name is used.

If the domain contains at least one * or ? character then the Object Name is a pattern. These characters cannot be used in the name of an MBean, only when matching MBeans.

The domain cannot contain the colon character ( : ).

The key-property-list in an MBean name contains one or more key properties. A key property looks like name= value, for example type=Thread . When there is more than one key property, they are separated with commas, for example type=Thread,name=DGC .

Spaces are significant everywhere in an Object Name. Do not write type=Thread, name=DGC (with a space after the comma) because it will be interpreted as having a key called " name ", with a leading space in the name.

Object Names are case sensitive.

The order of key properties is not significant. There is no difference between type=Thread,name=DGC and name=DGC,type=Thread .

The set of characters in a key is limited. It is recommended to stick to legal Java identifiers.

The set of characters in a value is also limited. If special characters may occur, it is recommended that the value be quoted, using ObjectName.quote. If the value for a given key is sometimes quoted, then it should always be quoted. By default, if a value is a string (rather than a number, say), then it should be quoted unless you are sure that it will never contain special characters. Here are some examples of Object Names containing quoted values:

com.sun.someapp:type=Whatsit,name="25" com.sun.someapp:type=Whatsit,name="25,26"

The second example would be illegal without quoting.

In a pattern, the key-property-list can have the same form as just described; or it can be a single * ; or it can be empty (equivalent to a * ); or it can be a list followed by ,* . These forms match Object Names that have the exact key properties given (if any) plus any arbitrary other key properties. For example, *:type=Thread,* matches somedomain:type=Thread and somedomain:type=Thread,name=DGC .

Object Name Conventions

Object Names should be predictable. This means that if you know that there is going to be an MBean representing a certain object, then you should be able to know what its name will be. The name should not contain properties that are not an inherent part of the object's identity. The name of the same object should not change between different executions of the application, so for example it should not contain items like a "JVM id" or the "MBean Server Id" that will change with each execution. (The concept of "the same object" is not always clear or meaningful, but when it is, the same object should always have the same name.)

The domain part of an Object Name should start with a Java package name. This prevents collisions between MBeans coming from different subsystems. There might be additional text after the package name. Examples:

com.sun.someapp:type=Whatsit,name=5 com.sun.appserv.Domain1:type=Whatever

The domain should not contain the character slash ( / ). That character is reserved for MBean Server hierarchies ("cascading"). This will be detailed further in a later version of this document.

Every Object Name should contain a type= key property. This property should be different for every object type in a given domain. JSR 77 (J2EE management) defines a j2eeType= key property with essentially the same semantics. Since JSR 77 allows arbitrary extra keys in addition to the standard ones it defines, it is possible to add a type= key in addition to the j2eeType= one. Note that allowing extra keys like this contradicts the predictability requirement stated above and is not recommended for new JMX models. It makes writing non-interactive clients unnecessarily difficult (you always have to query before accessing any object to discover its actual name).

Every Object Name with a given type should have the same set of key properties with the same syntax and semantics for their values.

If there can only be one instance of a given type in a given domain, there should not usually be any other key properties than type .

If there can be several instances of a given type, they can be differentiated either by being in different domains or by further key properties. Most often, domains are relatively fixed, and variable parts of names are in key properties.

The most usual key property in addition to type is name . Often, type=X,name=Y is enough to name an MBean. Some JMX-aware consoles are able to display this form of name using a shorthand that is easier to read than other names.

Sometimes it is useful to define additional properties as well as name , to facilitate searches using patterns as described above. For example, there might be a category or group property, allowing patterns such as:

com.sun.someapp:group=configuration,*

Object containment

Sometimes, there are managed objects that are logically contained in other managed objects. An object is logically contained in another if it cannot exist independently. In this case, the following scheme is often appropriate. Suppose you have Server objects which contain Application objects which contain WebModule objects which contain Servlet objects. Then their names would look like this:

domain:type=Server,name=server5 domain:type=Server.Application,Server=server5,name=app1 domain:type=Server.Application.WebModule,Server=server5, Application=app1,name=module3 domain:type=Server.Application.WebModule.Servlet,Server=server5, Application=app1,WebModule=module3,name=default

The hierachical type property makes it possible to understand the meaning of the other keys without a priori knowledge of the model. Since Object Name keys are unordered, it would otherwise not be possible to know whether for instance, in the third and fourth names here, an Application is contained in a Server or vice versa.

This scheme implies that if an object of a given type is sometimes contained in an object of another type, then it is always so contained. So here, for example, a Server.Application is always contained in a Server. If there are other Application objects that are not contained in Server objects, then they are of a different type, and that is reflected in the fact that their type property will not be Server.Application . This is consistent with the rule that a given type always implies the same list of other key properties.

Standard MBean interfaces

Most MBeans are specified using Standard MBean interfaces such as this:

public interface CacheControlMBean { int getSize() throws IOException; void setSize(int size) throws IOException; int getUsage() throws IOException; int dropOldest(int n) throws IOException; }

It is strongly recommended that MBeans be specified in this way where possible. This allows them to be documented using the familiar Javadoc tool, and it allows client code to interact with them straightforwardly via proxies, using MBeanServerInvocationHandler . Contrast the code without a proxy:

MBeanServer mbs = ...; Integer sizeI = (Integer) mbs.getAttribute(objectName, "Size"); int size = sizeI.intValue(); if (size > desiredSize) { mbs.invoke(objectName, "dropOldest", new Integer[] {new Integer(size - desiredSize)}, new String[] {"int"}); }

with the code that uses a proxy:

MBeanServer mbs = ...; CacheControlMBean cacheControl = (CacheControlMBean) MBeanServerInvocationHandler.newProxyInstance(mbs, objectName, CacheControlMBean.class, false); int size = cacheControl.getSize(); if (size > desiredSize) cacheControl.dropOldest(size - desiredSize);

The creation of the proxy is somewhat verbose, but once it is available, the MBean can be accessed like a local object. This is much easier to write and read, and much less error-prone, than accessing the MBeanServer method directly.

It is good practice for every method in a Standard MBean interface to be declared to throw java.io.IOException as shown here. This forces the code using the proxy to handle this exception explicitly. Otherwise, a communication problem while using the proxy will show up as the unchecked exception UndeclaredThrowableException wrapping the original IOException . If your MBean will only ever be accessed locally (from within the same Java Virtual Machine) then declaring IOException is not necessary, but this case is rare. One way to simplify local access is just to declare a subinterface that redeclares all the same methods but without IOException :

public interface LocalCacheControlMBean extends CacheControlMBean { int getSize(); void setSize(int size); int getUsage(); int dropOldest(int n); }

This is essentially the approach adopted in the JMX API itself, between the remote interface MBeanServerConnection and the local interface MBeanServer . The Java class that implements the interface can implement LocalCacheControlMBean rather than CacheControlMBean - this will help prevent having the two interfaces get out of sync.

Dynamic MBeans

Occasionally, Standard MBeans are not enough. Dynamic MBeans should be used when the interface of a managed object is not known at compile time. For example, you might have an MBean that represents an XML configuration file. Each configuration item is reflected by a read/write attribute in the MBean. The list of attributes for the MBean is constructed at runtime by examining the file.

It is almost never necessary to implement the DynamicMBean interface for an MBean whose management interface is known at compile time. If you need some particular abilities of Dynamic MBeans, for example the ability to supply descriptions for the attributes or operations, or the ability to disable certain attributes or operations, then you should consider subclassing javax.management.StandardMBean rather than implementing the DynamicMBean interface. That means that the management interface is still described by a Java interface, which in turn means that clients can use that interface to make a proxy for convenient access.

Model MBeans

Model MBeans are a particularly advanced feature of the JMX API. It is perfectly possible to produce an entirely adequate set of MBeans without ever using Model MBeans. Model MBeans are harder to program so they should only be used when there is a clear benefit.

The two main features of Model MBeans that are potentially useful are:

They add a level of indirection. This means that you can take an existing Java object that was not designed as an MBean, and expose one or more of its public methods as MBean operations. They associate additional information with the MBeanInfo and MBean*Info (e.g. MBeanAttributeInfo ) classes, in the form of descriptors.

The predefined Model MBean class RequiredModelMBean additionally can support features such as logging and persistence, but, because these are optional, portable code can not rely on them.

Item 1, concerning indirection, is usually a minor convenience. It may be useful when creating MBeans remotely, however, since the RequiredModelMBean class is guaranteed to be present, whereas a custom MBean class that exposes methods of another Java object might not be.

Item 2, concerning additional metadata, may be useful in certain applications.

The Jakarta Commons Modeler from the Apache Software Foundation builds on the Model MBean framework to allow you to specify MBean interfaces, including descriptors etc, using XML. The attributes and operations mentioned in XML are forwarded to an instance of a Java class that does not have to be an MBean class. The main advantage of this approach is perhaps that the entire information model can be contained in a single XML file. The main disadvantage is that there is no Java interface corresponding to the management interface of a given MBean, which means that clients cannot construct proxies.

Linking MBeans to resources

An MBean is not just an interface. There must be some behavior associated with that interface. Most often, that behavior depends on the application or resource being managed or monitored. In other words, the MBean must interact with other Java objects.

The simplest way to achieve this is to take an existing Java object that is part of the application and make it into an MBean. For example, you might take a Cache object and add a CacheMBean interface to it. Then your object is both an integral part of the application and a remotely-accessible management point.

From an object-oriented programming standpoint, it is preferable not to make the same object do two different things. Here the Cache object is both a cache and the management of a cache, which violates this principle. An alternative approach is for the MBean to be a separate object that has a reference to the application object. Very often this reference is passed as a parameter to the constructor of the MBean. As mentioned above, Model MBeans provide one way to do this. Another way is to code a Standard MBean as illustrated here:

public class Cache { // the original application object ... public int getSize() {...} ... } public class CacheManager implements CacheManagerMBean { private final Cache cache; public CacheManager(Cache cache) { this.cache = cache; } public int getSize() { return cache.getSize(); } ... } public interface CacheManagerMBean { public int getSize(); ... }

The original Cache object from the application is unaware of management concerns. It only needs to export enough methods to allow the separate CacheManager MBean to manage it.

Data types in MBeans

An MBean references data types in several places:

Every attribute has a type. In Standard MBeans, this is the return type of the getter method and/or the parameter type of the setter method.

Every operation has a return type, and a type for each of its parameters. In Standard MBeans, these types are the same as the return and parameter types of the corresponding Java method.

A getAttribute , setAttribute , or invoke can throw an exception. In Standard MBeans, these exceptions are thrown from the getter, setter, or operation method; if they are checked exceptions they must be declared in the throws clause of the method. However, the set of thrown exceptions is not in general visible to management clients.

, , or can throw an exception. In Standard MBeans, these exceptions are thrown from the getter, setter, or operation method; if they are checked exceptions they must be declared in the clause of the method. However, the set of thrown exceptions is not in general visible to management clients. A notification can be of any subtype of javax.management.Notification . In addition, its userData field can refer to a Java object of any reference type.

Complex data types

It is often necessary or desirable for these data types to be more complex than simple Java types such as int or String . In particular, when an attribute represents a snapshot of values such as the state of a thread at a given moment, it may not be meaningful to see a set of different values taken at different times. That could lead to meaningless results such as a thread that appears to be both running and waiting for a certain lock.

Using multiple attributes instead of complex data types

It is sometimes possible to address situations like this by breaking out the set of values into separate attributes and stipulating that those attributes must be read or written with a single getAttributes or setAttributes operation. But this tends to be a special-case solution that is difficult to implement and error-prone when clients fail to respect the requirement for using a single operation. It is also fragile in the face of evolution of the model. For example it does not extend to operation parameters or return values, and it does not work well if the values broken out into attributes are themselves complex types. So in general it is better to have a single data type representing the atomic set of values.

Using model-specific classes for complex data types

Complex data types can also be represented by defining model-specific types, for example a ThreadInfo type for a snapshot of a thread. But this solution presents its own problems. Chief among these is that the client must have the same Java classes available. This is very inconvenient for model-neutral clients such as generic JMX consoles. It also potentially causes problems for access from languages other than Java (for instance, from scripts). And it can lead to serious class-loading headaches if the same client must interact with servers that have different versions of the model-specific classes. Finally, using arbitrary Java types can lead to unforeseen problems with serialization, since when an object is serialized other objects that it references may also be serialized, meaning that a client must have the classes for those other objects available too.

Automatic class downloading

RMI can be configured to automatically download classes from server to client, or more rarely from client to server, that are not already present. Jini is fundamentally based on this idea, for example. However, this approach is generally not recommended for JMX-based management for a number of reasons:

It only works with the RMI connector defined by the JMX Remote API, not with the JMXMP connector defined there or with other currently non-standard connectors such as HTTP connectors.

It requires client and server to have access to a common code source. Typically, this is an HTTP server where the classes are installed. Setting up and maintaining this server is non-trivial.

The client (or server if it is the server that is downloading) must be configured with a security manager and it must be adjusted with exactly the right permissions. Getting the permissions wrong may mean that the downloaded code will not work, or may expose the client to security attacks.

Third parties are generally suspicious of code downloading. If the JMX model is exposed as a public interface, and if people are told that to use it they must configure their security to allow code downloading, this will meet with resistance.

The approach does not map well to access from non-Java clients such as scripts.

The approach typically requires client and server to share at least a Java interface describing the data type, so what is downloaded is an implementation of this interface. In the cases we are talking about, the classes are value classes and there is little to be gained by this separation of interface and implementation. Furthermore, the interface classes still have to be shared between client and server, with the problems already discussed above.

Open MBeans

The JMX API addresses the question of complex data types with Open MBeans. A normal MBean has an MBeanInfo that describes the names and types of its attributes, operations, and notifications. An Open MBean defines an OpenMBeanInfo that supplements this information with an OpenType for each attribute and operation type in addition to its Java type. Only a predefined set of Java classes can be described with Open Types. Thus, if an MBean is an Open MBean, then any client can interact with its attributes and operations, without needing model-specific classes. Additionally, the OpenMBeanInfo can describe allowed and default values for attributes, parameters, and return values.

The classes that can be described in an Open Type are these:

Primitive wrapper classes from java.lang : Integer , Boolean , Void etc.

: , , etc. The classes String , Date , ObjectName , BigDecimal , BigInteger .

, , , , . CompositeData , described below.

, described below. TabularData , described below.

, described below. An array of any of the classes in this list.

Notice that primitive types such as int are not in this list. In practice there is no difference to a client between an access to int and an access to java.lang.Integer , because everything goes through reflection; but there is a difference between int[] and Integer[] . We expect that the next version of the JMX spec will allow primitive types (and arrays thereof) in Open MBeans; see bug 5045358.

CompositeData

The Java type of an attribute, parameter, or return value in an Open MBean can be CompositeData . In this case, the corresponding OpenType in the OpenMBeanInfo will be CompositeType . The CompositeType defines a set of items or fields. Each item has a name and an associated type, which is an Open Type. Thus, a complex thread state attribute can be represented in an Open MBean by an attribute whose Java type is CompositeData and whose corresponding Open Type is a CompositeType that describes the names and types of the thread state items.

TabularData

The Java type of an attribute, parameter, or return value in an Open MBean can be TabularData . In this case, the corresponding OpenType in the OpenMBeanInfo will be TabularType . The TabularType describes a table in which each row is a CompositeData (with a single CompositeType describing all rows), and one or more items in the CompositeData are identified as forming the key for the table.

Recommendations

Coding Open MBeans leads to guaranteed interoperability with respect to data types.

Coding Open MBeans is currently rather awkward, because they must be Dynamic MBeans. Thus, an alternative is to write MBeans that use only the Open MBean data types ( CompositeData etc), but are not actually Open MBeans (their MBeanInfo is not an OpenMBeanInfo ). Such MBeans can be coded as Standard MBeans.

For example, instead of referring to ThreadInfo in your MBean, you would refer to CompositeData . The CompositeData in question would have the same fields ( name, blockedCount, suspended) as the original ThreadInfo . For convenience, you can add conversion methods to the ThreadInfo class:

public CompositeData toCompositeData(); public static ThreadInfo from(CompositeData cd);

It is possible to automate the conversion from arbitrary Java classes (with certain constraints) to Open-MBean-compatible classes.

Java SE 6 will add user-defined MXBeans. This makes it possible to use any Java type that respects a certain number of constraints. It converts types into Open Types such as CompositeType where necessary. MXBeans already exist in java.lang.management in version 5.0 (Tiger) of the Java platform, but only a fixed set is defined.

Datatypes for notifications

Notifications should be instances of javax.management.Notification or one of the subclasses from the javax.management namespace. Information that does not fit into one of those subclasses should be conveyed by attaching a CompositeData to the notification using the setUserData method.

There is unfortunately no OpenMBeanNotificationInfo class today (see bug 5072197). If such a class existed, it could be used to indicate what the CompositeData in the userData for the notification(s) emitted by an MBean would look like.

A client can send an arbitrary NotificationFilter to a server in an addNotificationListener operation. However, using an application-specific filter class is subject to the same sorts of problems as detailed for model-specific classes above. It is recommended that only the standard filters defined in javax.management.* be used. ( NotificationFilterSupport , AttributeChangeNotificationFilter , MBeanServerNotificationFilter .)

Exceptions

It is recommended that exceptions thrown by MBeans be drawn from the standard set defined in the java.* and javax.* packages on the Java SE platform. If an MBean throws a non-standard exception, a client that does not have that exception class will likely see another exception such as ClassNotFoundException instead.

Self-referencing datatypes

Occasionally, an information model may contain data types that are self-referencing, that is where the definition of the type includes, directly or indirectly, a reference to the type itself. For example:

public interface Graph { public Node[] getNodes(); } public interface Node { public Node[] getNeighbours(); }

There is no way to represent such a type directly using Open Types. This is a consequence of the fact that the classes representing Open Types are immutable. The problem can be avoided by adding a level of indirection. In the example, every Node could be given a name:

public interface Graph { public Map<String,Node> getNodes(); } public interface Node { public String[] getNeighbours(); }

This can now be mapped to Open Types, using a TabularData to represent the Map<String,Node> .

Evolution of MBean interfaces

public interface CacheControl2MBean extends CacheControlMBean { void saveConfiguration() throws IOException; }

If your API includes a Standard MBean interface, then you must specify whether or not users are allowed write their own Java classes that implement it. If they are, then almost any change to the interface will risk breaking user classes that implemented it. So it is safest to spell out that the interface is only intended to specify the MBean's attributes and operations, or to facilitate the creation of proxies (for example with). Otherwise, you can still add new attributes and operations by putting them in a subinterface and leaving the original interface intact:

Given that you have specified that users must not implement your interface, the changes that you can make to it are essentially:

adding a new operation

overloading an existing operation (but overloading is not recommended in general)

changing the type of a write-only attribute from a class to a superclass or superinterface (for example from String to Object or from HashMap to Map ), or from a primitive type to a wider primitive type (for example from int to long )

to or from to ), or from a primitive type to a wider primitive type (for example from to ) changing the type of a read-only attribute or the return type of an operation from a class to a subclass (for example from Object to String )

to ) adding or removing unchecked exceptions in the throws clause.

You cannot do any of the following, in each case because it would break code that accesses the MBean through MBeanServer operations such as getAttribute and invoke , and/or because it would break compilation or execution of code that accesses the MBean through a dynamic proxy using MBeanServerInvocationHandler :

removing an attribute or operation

changing the type of a write-only attribute in any way other than specified above

changing the type of a read-only attribute or the return type of an operation in any way other than specified above

changing the type of a read-write attribute

changing the parameters of an operation in any way

adding or removing checked exceptions in the throws clause (this includes replacing an exception by a subclass or superclass).

If your MBean is a Dynamic MBean and does not have a Standard MBean interface that clients can use to make a dynamic proxy, then the above restrictions are slightly relaxed and the following become possible:

changing the type of a write-only attribute in any way, provided that your MBean can still deal with the old type

changing the type of a read-write attribute from a class to a subclass, again provided that your MBean can still deal with the old type when the attribute is set

changing the parameters of an operation, provided that your MBean can still deal with the old parameters

there is no such thing as a throws clause in this case so the restriction on that is irrelevant.

However it is strongly recommended to provide Standard MBean interfaces where possible because of the substantial increase in usability.

Evolution of complex types

As described above, an MBean may use complex types in attributes, notifications, and operation parameters and return values. It is recommended that Open Types be used, which for complex types means ArrayType , CompositeType , or TabularType .

Where one of these complex types appears in a model, it cannot in general be changed in a subsequent version. However, the contents of the type can be changed. So for example, in version 1 of a model, getThreadInfo might return a CompositeData with items name, blockedCount, suspended; while in version 2, getThreadInfo still returns a CompositeData , but it now also has a blockedTime item in addition to the previous ones. A version 1 client or server that gets the CompositeData from a version 2 peer will simply ignore the new item. A version 2 client or server that gets the CompositeData from a version 1 peer must be able to cope with the missing blockedTime item.

Arrays have no real structure, so there are no particular rules for their evolution. If the description of a model explicitly states constraints on the length of an array (for example, it is always 3, or it is always at most 8), then those constraints cannot usually be violated in a subsequent version without breaking existing code.

A TabularType is characterized by its rowType and its indexNames . The indexNames cannot change in a compatible evolution, because an older peer might try to call get(key) with a key based on the former indexNames . The rowType , which is a CompositeType , can change in accordance with the rules for CompositeType detailed next.

A CompositeType can evolve by adding new items. An older peer will ignore these items. This is not completely bullet-proof, because the older peer might call CompositeData.values() and be surprised to see the extra values; or it might call oldCompositeType.isValue(newCompositeData) which will currently return false because of the extra items. ( CompositeData.values() will probably be deprecated in a future version of the JMX API since it is of little use in practice.)

When a server adds new items, clients that know about the new items can refer to them, but they must also be prepared to handle the case when the new items are missing, because they are talking to an older server. Likewise when the server expects a CompositeData to which new items have been added, it must be prepared to receive a CompositeData from an older client, where the new items are missing.

Notifications

Source

Although the JMX API allows any object to be used as the source field of a notification, in practice it should be one of the following:

an ObjectName, usually that of the MBean that constructed the Notification object

a reference to the MBean object that is sending the notification.

When the MBean Server is about to forward a notification whose source is a reference to the MBean sending the notification, it changes the source into the MBean's ObjectName. In this case, if the notification is forwarded to another MBean and then resent by that MBean, the MBean Server will not rewrite the source. So relying on this rewriting is discouraged.

Semantics of notification delivery

It is important to be aware of the semantics of notification delivery when defining how notifications are used in a model. Remote clients cannot assume that they will receive all notifications for which they are listening. The JMX Remote API only guarantees a weaker condition:

A client either receives all notifications for which it is listening, or can discover that notifications may have been lost.

A consequence of this weaker guarantee is that notifications should never be used to deliver information that is not also available in another way. The typical client observes the initial state of the information model, then reacts to changes in the model signalled by notifications. If it sees that notifications may have been lost, it goes back and observes the state of the model again using the same logic as it used initially. The information model must be designed so that this is always possible. Losing a notification must not mean losing information irretrievably.

When a notification signals an event that might require intervention from the client, the client should be able to retrieve the information needed to react. This might be an attribute in an MBean that contains the same information as was included in the notification. If the information is just that a certain event occurred, it is often enough just to have a counter of how many times it occurred. Then a client can detect that the event occurred just by seeing that the counter has changed.

A client can discover when notifications are lost by registering a listener using JMXConnector.addConnectionNotificationListener .

Large data values

The existing JMX protocols are not very well adapted to moving about large data values (on the order of tens of thousands of bytes or more). This is true for notification payloads as well as for attribute values being read or written and for parameters and return values of operations. Although large data values will work, there may be an effect on concurrent access using the same connector client. It is often preferable to send a URL that allows the other end (client or server) to access the large data value directly.

This is particularly important for notifications. Because of the way notification sending works in the current protocols, a notification may remain in the server for an indefinite period after being sent. Thus, if many large notifications are sent (for example because they have a very big string or byte array in the userData field), an OutOfMemoryError may result.

Related documentation

The article Apply JMX Best Practices from the Java Pro journal provides a set of higher-level recommendations that complement the ones in this document.