In this chapter, we will discuss a facility provided by JMX that allows MBeans to be loaded into an agent dynamically. This facility, called the M-Let (short for management applet) service, is the first agent-level service we have discussed so far. There are two major sections in this chapter. The first section is an overview of the M-Let service, including the various facets of it that make it work. The second section deals with the details of the M-Let service and provides examples of code that executes in the JMX agent that uses the M-Let service.
In this section, we will look at the M-Let service, whose purpose is
to provide an agent with a means to load MBeans from a Universal
Resource Locator (URL). There are two ways that an agent can use the
M-Let service to accomplish this. First, the agent can specify an
M-Let file to the M-Let service, which uses the
contents of this file to load the MBeans. The M-Let file is an
XML-like text file that contains various tags that describe the
MBeans to be loaded. The second method of loading MBeans is to use
the M-Let service itself to load the MBeans without the use of an
M-Let file. The M-Let service extends
URLClassLoader
from the
java.net
package and is thus capable of fetching
bytecode from any valid URL into the JVM in which the agent is
running.
In the RI, the M-Let
service is implemented in a class called MLet
,
which implements an interface called MLetMBean
(so
it is instrumented as a standard MBean). The
MLetMBean
interface allows agents (and management
applications) to manipulate the M-Let service to load MBeans and to
manage the M-Let service itself. The MLetMBean
interface is
defined as:
public interface MLetMBean { public Set getMBeansFromURL(String url) throws ServiceNotFoundException; public Set getMBeansFromURL(URL url) throws ServiceNotFoundException; public void addURL(URL url); public void addURL(String url) throws ServiceNotFoundException; public URL[] getURLs( ); public URL getResource(String name); public InputStream getResourceAsStream(String name); public Enumeration getResources(String name) throws IOException; public String getLibraryDirectory( ); public void setLibraryDirectory(String libdir); }
In this section, we will discuss only those methods that are part of
the MLetMBean
interface. Primary emphasis will be
placed on those methods that are mentioned in the specification, with
secondary emphasis placed on the others.
There are two methods of primary concern when using
the M-Let service: getMBeansFromURL(
)
and addURL(
)
. The getMBeansFromURL(
) method has two versions: the first takes a
String
that contains the URL of the text file that
describes the MBeans to load, and the second takes a
URL
object that contains the URL of the M-Let
file. The addURL( ) method is used to add a URL
to the list of URLs that are to be searched when loading MBeans while
using the M-Let service as the class loader. These two methods are
the ones you will use when writing agents that use dynamic loading as
a part of your MBean deployment strategy.
The other methods on the MLetMBean
interface
provide functionality that you would expect to see in a class loader.
For example, the getURLs(
)
method returns an array of the
URL
objects that are searched when loading classes
and resources, and the getResourceAsStream(
)
method takes a String
containing the name of a resource and returns an
InputStream
object so the resource can be read.
The M-Let service must be created and registered with the MBean
server before you can use it. The examples that follow assume that a
reference to the MBean server has been obtained (we saw how to do
this in earlier chapters) and that the M-Let service is created by
simply using the Java new
keyword on the RI class
MLet
. The MLet
class implements
the MBeanRegistration
interface, so it is capable
of creating its own object name. In the examples that follow, we will
allow it to do so.
The M-Let file is a text file that looks like XML but is not required to be well-formed XML. Each of the components of the M-Let file is called a tag (even though the “tag” may resemble an XML attribute; remember, it’s not well-formed XML) The JMX specification defines several tags that are used in the M-Let file, which we will look at in this section. The format of the M-Let file is:
<MLET CODE="className
" | OBJECT="serializedObjectFileName
" ARCHIVE="classOrJarFileName
" [CODEBASE="relativePathToArchive
"] [NAME="mbeanObjectName
"] [VERSION="version
"] > [<ARG TYPE="type
" VALUE="value
">] </MLET> . . .
There is one MLET
tag for each MBean to be loaded.
For example, if there were five MBeans to load, there would be five
MLET
tags in the M-Let file. Each MBean specified
in the M-Let file is required to provide either the full string
representation of its class name (by using the
CODE
tag) or the name of a file that contains the
MBean’s serialized state (by using the
OBJECT
tag). The CODE
and
OBJECT
tags are mutually exclusive (i.e., for any
given MBean, one or the other may be specified, but not both). In
addition, the name of the JAR file in which the bytecode is archived
must be specified. The other tags are not required.
Let’s look at each of these tags in detail.
As we mentioned, each MBean to be loaded by the
M-Let service must have its own MLET
tag in the
M-Let file. It’s as simple as that.
The value of this tag is designated by
className
in the example above and must be
the string representation of the MBean’s class name.
For example, suppose the MBean’s class name is
sample.mlet_loadable.Queue
. The
CODE
tag would then look like:
CODE="sample.mlet_loadable.Queue"
If we had simply specified "Queue"
as the
CODE
value, the M-Let service would not be able to
locate the bytecode for our MBean class. As you might expect, the
M-Let service must be able to locate this class relative to one of
the URLs that it is using as its search path. We will see how to set
this URL later.
The value of this tag is designated by
serializedObjectFileName
in the example
above and is the name of the file that contains the
MBean’s serialized state. Suppose that we serialized
the state of the Queue
class in a file named
Queue.ser
. We would then instruct the M-Let
service to load the MBean from that file:
OBJECT="Queue.ser"
Of course, the M-Let service must be able to locate this file relative to one of the URLs that it is using as its search path.
The value of this tag is designated by
classOrJarFileName
in the example above
and is the names of one or more JAR files, one of which contains the
.class
file for the MBean. Suppose the JAR that
contains the Queue
class is called
mlet_loadable.jar
. In this case, the
ARCHIVE
tag would look like:
ARCHIVE="mlet_loadable.jar"
Multiple JAR files are separated by commas:
ARCHIVE="mlet_loadable.jar,another.jar,yetanother.jar"
The M-Let service will search the URLs that constitute its search
path for all of the JAR files that are specified by the
ARCHIVE
tag. At least one of the JAR files must
contain the bytecode for the MBean.
The value of this tag is designated by
relativePathToArchive
in the example above
and is the relative path to the JAR file specified by the
ARCHIVE
tag. But relative to what? The M-Let
service uses the URL of the M-Let file as the default URL (minus the
M-Let filename, of course) to the JAR file specified by
ARCHIVE
. If no CODEBASE
tag is
specified, the default URL is used as the code base from which to
load the bytecode for the MBean. This is useful when the JAR file is
located in the same directory as the M-Let file.
Suppose that the URL to the M-Let file is
http://myserver/mbeans/mbeans.txt. The default
URL in this case is http://myserver/mbeans. Now
suppose that we specify the value of the ARCHIVE
tag to be mlet_loadable.jar
, located at
http://myserver/mbeans/jars, and we do not
provide a CODEBASE
tag. The M-Let service will use
the default as the base URL for locating
mlet_loadable.jar
. It will try to load
http://myserver/mbeans/mlet_loadable.jar, but it
will not be able to find it.
However, if we specify a CODEBASE
value relative
to the default URL:
CODEBASE="jars"
the M-Let service will add the CODEBASE
value to
the default URL, resulting in
http://myserver/mbeans/jars/mlet_loadable.jar,
and the JAR file will be located. Because the
CODEBASE
value is added to the default URL,
specifying:
CODEBASE="."
and omitting the CODEBASE
tag altogether have the
same effect. As you might expect, you can use "."
and ".."
to represent the current directory and
parent directory, respectively. Suppose that instead of
mlet_loadble.jar
being subordinate to the M-Let
file, the two files are located in peer directories, with
mlet_loadable.jar
being located at
http://myserver/jars. In this case, the
CODEBASE
tag would have to be specified as:
CODEBASE="../jars"
The value of this tag is designated by
mbeanObjectName
in the example above and
is the string representation of the object name for the MBean.
Suppose the object name string for the Queue
class
is ":name=Queue,loadedFrom=MLET"
, where the domain
is the default domain. The NAME
tag could then be
specified as:
NAME=":name=Queue,loadedFrom=MLET"
When the Queue
MBean is loaded by the M-Let
service, it will be given this object name. If the object name
already exists, the MBean will not be loaded and an exception will be
returned to the agent that is using the M-Let service.
If this tag is omitted, the M-Let service assumes that the MBean
implements the MBeanRegistration
interface and
will provide its own object name.
The value of this tag is designated by
version
in the example above and
represents the version of the JAR file specified by
ARCHIVE
and/or the MBean to be loaded. The primary
purpose of this tag is to support versioning and caching in the
implementation. The format of this tag is one or more nonnegative
integers separated by a dot (.):
VERSION="1.0.1"
Note that the JMX 1.0 RI does not support this tag. Support for the
VERSION
tag will most likely be present in a
future release of the JMX RI.
This tag
represents an argument that is to be passed to the constructor of the
MBean when it is loaded and instantiated. The tags that accompany
this tag are TYPE
and VALUE
,
which represent the argument’s data type and its
value, respectively. Only fundamental types
(boolean
, byte
,
char
, short
,
int
, long
,
float
, and double
),
java.lang
fundamental wrapper types
(Boolean
, Byte
,
Char
, Short
,
Int
, Long
,
Float
, Double
, and
String
) are supported, as they may all have a
string representation (unlike complex user-defined types). The
ARG
tag must follow the closing
>
of the MLET
tag.
Using the Queue
class, which has an alternate
constructor that takes a single int
to set the
queue depth, we can specify a single ARG
tag to
set the queue depth to seven items:
<ARG TYPE="int" VALUE="7">
Multiple arguments to the MBean constructor may be specified. The
order of the arguments in the M-Let file must correspond to the order
of the arguments to the constructor. Suppose that a constructor takes
a String
, a float
, and an
Integer
, in that order. The ARG
tags must also be supplied in that order:
<ARG TYPE="java.lang.String" VALUE="Hello, world"> <ARG TYPE="float" VALUE="3.14159"> <ARG TYPE="java.lang.Integer" VALUE="104">
Notice that the JDK wrapper classes String
and
Integer
must be fully qualified. If we had written
the ARG
tags as:
<ARG TYPE="String" VALUE="Hello, world"> <ARG TYPE="float" VALUE="3.14159"> <ARG TYPE="Integer" VALUE="104">
the MBean would not be loaded, because the M-Let service cannot fetch
the bytecode for the String
and
Integer
parameters. However, fundamental types
simply require the name of the type, as that is the name of the
Class
object that represents fundamental type
inside the
JVM.
Now that
we’re familiar with the
tags that can be used in the M-Let file, let’s look
at a simple example. Suppose that we want to load the
sample.mlet_loadable.Queue
MBean from
mlet_loadable.jar
, giving it the name
":name=Queue,loadedFrom=MLET"
and passing an
int
argument value of 8 to its constructor:
<MLET CODE="sample.mlet_loadable.Queue" ARCHIVE="mlet_loadable.jar" NAME=":name=Queue,loadedFrom=MLET" > <ARG TYPE="int" VALUE="8"> </MLET>
We will see later exactly how to use the M-Let file, the URL
describing its location, and the getMBeansFromURL(
) method of the MLetMBean
interface to
load the MBeans.
What about comments
in the M-Let file? The specification does
not mention them, so it’s not a good idea to expect
support for comments to be in every implementation of JMX. However,
in the JMX 1.0 RI, the parser that reads the M-Let file allows for
any text to be placed in the file as long as it is outside of a
“< . . . >” construct. In
other words, no text other than the tags we have discussed is allowed
anywhere inside the <MLET . . . >
tag, the
</MLET>
closing tag, or the <ARG . . . >
tag. You can place whatever text you like
outside of those tags. For example:
This text will be ignored by the parser <MLET Oops, text cannot go here! CODE="sample.mlet_loadable.Queue" ARCHIVE="mlet_loadable.jar" NAME=":name=Queue,loadedFrom=MLET" > This text is ignored This text is ignored <ARG TYPE="int" VALUE="8"> </MLET>
The line of text following the MLET
opening tag
will cause the parser to report an error with the M-Let file. All of
the other text will be ignored by the parser.
As mentioned
earlier,
the MLet
class, which is the RI’s
implementation of the M-Let service, is a class loader capable of
fetching bytecode from a URL and creating a Class
object for an MBean. We have already looked at how the M-Let service
uses its class loader functionality in conjunction with an M-Let file
to load MBeans. In this section, we will see how to use the M-Let
service to load MBeans without the use of an M-Let file.
The MLetMBean
interface—implemented by the
MLet
class—provides a method that allows an
agent to add one or more URLs that the M-Let service will search when
loading MBeans. This method, addURL( ), works in
conjunction with the MBean server methods instantiate(
) and createMBean( ) to load MBeans
from a URL. instantiate( ) and
createMBean( ) each have two versions that take
as a parameter the object name of the loader to be used when fetching
the bytecode for the MBean to be loaded. Once the URL of the JAR file
containing the MBean(s) to be loaded has been added to the M-Let
service’s search list of URLs, either
instantiate( ) or createMBean(
) can be called to load the MBean. We will see how to do
this later in this chapter.
If the MBean to be loaded exists in the same code base (i.e., one or more JAR files, specified by a URL) as any other MBean that has been loaded using an M-Let file, you do not need to specify the URL. In other words, the M-Let service remembers any URL from which it has previously loaded a class. This functionality is typical of all class loaders. We will look at an example of this later in this chapter.