Modularly developed from the ground up, the JBoss server and container are completely implemented using component-based plug-ins. The modularization effort is supported by the use of JMX, the Java Management Extension API. Using JMX, industry-standard interfaces help manage both JBoss/Server components and the applications deployed on it. Ease of use is still the number one priority, and the JBoss Server version 3.x architecture sets a new standard for modular, plug-in design as well as ease of server and application management.
This high degree of modularity benefits the application developer in several ways. The already tight code can be further trimmed down to support applications that must have a small footprint. For example, if EJB passivation is unnecessary in your application, simply take the feature out of the server. If you later decide to deploy the same application under an Application Service Provider (ASP) model, simply enable the server's passivation feature for that Web-based deployment. Another example is the freedom you have to drop your favorite object to relational database (O-R) mapping tool, such as TOPLink, directly into the container.
This chapter will introduce you to JMX and its role as the JBoss server component bus. You will also be introduced to the JBoss MBean service notion that adds life cycle operations to the basic JMX management component.
The success of the full Open Source J2EE stack lies with the use of JMX (Java Management Extension). JMX is the best tool for integration of software. It provides a common spine that allows the user to integrate modules, containers, and plug-ins. See The JBoss JMX integration bus and the standard JBoss components. shows the role of JMX as an integration spine or bus into which components plug. Components are declared as MBean services that are then loaded into JBoss. The components may subsequently be administered using JMX.
Before looking at how JBoss uses JMX as its component bus, it would help to get a basic overview what JMX is by touching on some of its key aspects.
JMX components are defined by the Java Management Extensions Instrumentation and Agent Specification, v1.0, which is available from the JSR003 Web page at http://jcp.org/aboutJava/communityprocess/final/jsr003/index.html. The material in this JMX overview section is derived from JMX instrumentation specification, with a focus on the aspects most used by JBoss. A more comprehensive discussion of JMX and its application can be found in JMX: Managing J2EE with Java Management Extensions written by Juha Lindfors (Sams, 0672322889, 2002).
JMX is about providing a standard for managing and monitoring all varieties of software and hardware components from Java. Further, JMX aims to provide integration with the large number of existing management standards. See The Relationship between the components of the JMX architecture. shows examples of components found in a JMX environment, and illustrates the relationship between them as well as how they relate to the three levels of the JMX model. The three levels are:
The instrumentation level defines the requirements for implementing JMX manageable resources. A JMX manageable resource can be virtually anything, including applications, service components, devices, and so on. The manageable resource exposes a Java object or wrapper that describes its manageable features, which makes the resource instrumented so that it can be managed by JMX-compliant applications.
The user provides the instrumentation of a given resource using one or more managed beans, or MBeans. There are four varieties of MBean implementations: standard, dynamic, model, and open. The differences between the various MBean types is discussed in Managed Beans or MBeans.
The instrumentation level also specifies a notification mechanism. The purpose of the notification mechanism is to allow MBeans to communicate changes with their environment. This is similar to the JavaBean property change notification mechanism, and can be used for attribute change notifications, state change notifications, and so on.
The agent level defines the requirements for implementing agents. Agents are responsible for controlling and exposing the managed resources that are registered with the agent. By default, management agents are located on the same hosts as their resources. This collocation is not a requirement.
The agent requirements make use of the instrumentation level to define a standard MBeanServer management agent, supporting services, and a communications connector. JBoss provides both an html adaptor as well as an RMI adaptor.
The JMX agent can be located in the hardware that hosts the JMX manageable resources when a Java Virtual Machine (JVM) is available. This is currently how the JBoss server uses the MBeanServer . A JMX agent does not need to know which resources it will serve. JMX manageable resources may use any JMX agent that offers the services it requires.
Managers interact with an agent's MBeans through a protocol adaptor or connector, as described in the See Distributed Services Level. in the next section. The agent does not need to know anything about the connectors or management applications that interact with the agent and its MBeans.
JMX agents run on the Java 2 Platform Standard Edition. The goal of the JMX specification is to allow agents to run on platforms like PersonalJava and EmbeddedJava, once these are compatible with the Java 2 platform.
The JMX specification notes that a complete definition of the distributed services level is beyond the scope of the initial version of the JMX specification. This was indicated by the component boxes with the horizontal lines in See The Relationship between the components of the JMX architecture.. The general purpose of this level is to define the interfaces required for implementing JMX management applications or managers. The following points highlight the intended functionality of the distributed services level as discussed in the current JMX specification.
It is intended that the distributed services level components will allow for cooperative management of networks of agents and their resources. These components can be expanded to provide a complete management application.
This section offers an overview of the instrumentation and agent level components. The instrumentation level components include the following:
The agent level components include:
An MBean is a Java object that implements one of the standard MBean interfaces and follows the associated design patterns. The MBean for a resource exposes all necessary information and operations that a management application needs to control the resource.
The scope of the management interface of an MBean includes the following:
JMX defines four types of MBeans to support different instrumentation needs:
We will present an example of a Standard and a Model MBean in the section that discusses extending JBoss with your own custom services.
JMX Notifications are an extension of the Java event model. Both the MBeanServer and MBeans can send notifications to provide information. The JMX specification defines the javax.management package Notification event object, NotificationBroadcaster event sender, and NotificationListener event receiver interfaces. The specification also defines the MBeanServer operations that allow for the registration of notification listeners.
There is a collection of metadata classes that describe the management interface of an MBean. Users can obtain a common metadata view of any of the four MBean types by querying the MBeanServer with which the MBeans are registered. The metadata classes cover an MBean's attributes, operations, notifications, and constructors. For each of these, the metadata includes a name, a description, and its particular characteristics. For example, one characteristic of an attribute is whether it is readable, writable, or both. The metadata for an operation contains the signature of its parameter and return types.
The different types of MBeans extend the metadata classes to be able to provide additional information as required. This common inheritance makes the standard information available regardless of the type of MBean. A management application that knows how to access the extended information of a particular type of MBean is able to do so.
A key component of the agent level is the managed bean server. Its functionality is exposed through an instance of the javax.management.MBeanServer . An MBeanServer is a registry for MBeans that makes the MBean management interface available for use by management applications. The MBean never directly exposes the MBean object itself; rather, its management interface is exposed through metadata and operations available in the MBeanServer interface. This provides a loose coupling between management applications and the MBeans they manage.
MBeans can be instantiated and registered with the MBeanServer by the following:
When you register an MBean, you must assign it a unique object name. The object name then becomes the unique handle by which management applications identify the object on which to perform management operations. The operations available on MBeans through the MBeanServer include the following:
Protocol adaptors and connectors are required to access the MBeanServer from outside the agent's JVM. Each adaptor provides a view via its protocol of all MBeans registered in the MBeanServer the adaptor connects to. An example adaptor is an HTML adaptor that allows for the inspection and editing of MBeans using a Web browser. As was indicated in See The Relationship between the components of the JMX architecture., there are no protocol adaptors defined by the current JMX specification. Later versions of the specification will address the need for remote access protocols in standard ways.
A connector is an interface used by management applications to provide a common API for accessing the MBeanServer in a manner that is independent of the underlying communication protocol. Each connector type provides the same remote interface over a different protocol. This allows a remote management application to connect to an agent transparently through the network, regardless of the protocol. The specification of the remote management interface will be addressed in a future version of the JMX specification.
Adaptors and connectors make all MBean server operations available to a remote management application. For an agent to be manageable from outside of its JVM, it must include at least one protocol adaptor or connector. JBoss currently includes a custom HTML adaptor implementation and a custom JBoss RMI adaptor.
The JMX agent services are objects that support standard operations on the MBeans registered in the MBean server. The inclusion of supporting management services helps you build more powerful management solutions. Agent services are often themselves MBeans, which allow the agent and their functionality to be controlled through the MBean server. The JMX specification defines the following agent services:
Any JMX-compliant implementation will provide all of these agent services. JBoss currently does not rely on any of these standard agent services.
JBoss 3.x employs a new class loading architecture that facilitates sharing of classes across deployment units. In JBoss 2.x it was difficult to have MBean services interact with dynamically deployed J2EE components, and MBeans themselves were not hot deployable. In JBoss 3.x, everything is hot deployable, and the new deployment architecture and class loading architecture makes this possible. Before discussing the JBoss specific class loading model, we need to understand the nature of Java's type system and how ClassLoaders fit in.
Class loading is a fundamental part of all server architectures. Arbitrary services and their supporting classes must be loaded into the server framework. This can be problematic due to the strongly typed nature of Java. Most developers know that the type of a class in Java is a function of the fully qualified name of the class. As of Java 1.2, the type is also a function of the java.lang.ClassLoader that is used to define that class. This additional qualification of type was added to ensure that environments in which classes may be loaded from arbitrary locations would be type-safe. A paper entitled "Java is not type-safe" by Vijay Saraswat in 1997 demonstrated that Java was not type-safe as intended. This could allow one to gain access to method and members of a class to which they should not have had access by fooling the Java VM into using an alternate implementation of a previously loaded class. Such circumvention of the type system was based on introducing class loaders that by-pass the normal delegation model. A ClassLoader uses a delegation model to search for classes and resources. Each instance of ClassLoader has an associated parent class loader that is either explicitly set when the ClassLoader is created, or assigned by the VM if no parent was specified. When called upon to find a class, a ClassLoader will typically delegate the search for the class to its parent class loader before attempting to find the class or resource itself. The VM has a root class loader, called the bootstrap class loader, does not have a parent but may serve as the parent of a ClassLoader instance.
To address the type-safety issue, the type system was strengthened to include a class's defining ClassLoader in addition to the name of the class to fully define the type. The original paper in which the solution was described is "Dynamic Class Loading in the Java Virtual Machine", by Sheng Liang and Gilad Bracha, and can be obtained from http://java.sun.com/people/sl/papers/oopsla98.ps.gz . The ramifications of this change in a dynamic environment like an application server, and especially JBoss with its support for hot deployment are that ClassCastExceptions , LinkageErrors , and IllegalAccessErrors can show up in ways not seen in more static class loading contexts. Let's take a look at the meaning of each of these exceptions and how they can happen.
A java.lang.ClassCastException results whenever an attempt is made to cast an instance to an incompatible type. A simple example is trying to obtain a java.lang.String from a java.util.ArrayList into which a java.net.URL was placed:
ArrayList array = new ArrayList();
array.add(new URL("file:/tmp"));
String url = (String) array.get(0);
java.lang.ClassCastException: java.net.URL
at org.jboss.chap2.ex0.ExCCEa.main(Ex1CCE.java:16)
The ClassCastException tells you that the attempt to cast the array element to a String failed because the actual type was java.net.URL . This trivial case is not what we are interested in however. Consider the case of a jar being loaded by different URLClassLoader s. Although the classes loaded through each URLClassLoader are identical in terms of the bytecode, they are completely different types as viewed by the Java type system. An example of this is illustrated by the code shown in See The ExCCEc class used to demonstrate ClassCastException due to duplicate class loaders..
* @author [email protected]
public class ExObj implements Serializable
public ExObj2 ivar = new ExObj2();
----------------------------------------------------------------------------
* @author [email protected]
public class ExObj2 implements Serializable
The ExCCEc.main method uses reflection to isolate the classes that are being loaded by the URLClassLoaders ucl0 and ucl1 from the application class loader. Both are setup to load classes from the output/chap2/j0.jar, the contents of which are:
[nr@toki examples]$ jar -tf output/chap2/j0.jar
org/jboss/chap2/ex0/ExCtx.class
org/jboss/chap2/ex0/ExObj.class
org/jboss/chap2/ex0/ExObj2.class
We will run an example that demonstrates how a ClassCastException can occur and then look at the specific issue with the example. See Appendix C for instructions on installing the examples accompanying the book, and then run the example from within the examples directory using the following command:
[nr@toki examples]$ ant -Dchap=chap2 -Dex=0c run-example
[java] [ERROR,UCL1] Failed to invoke ExCtx.useValue
[java] java.lang.reflect.InvocationTargetException
[java] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[java] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
[java] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
[java] at java.lang.reflect.Method.invoke(Method.java:324)
[java] at org.jboss.chap2.ex0.ExCCEc.main(ExCCEc.java:58)
[java] Caused by: java.lang.ClassCastException
[java] at org.jboss.chap2.ex0.ExCtx.useValue(ExCtx.java:44)
Only the exception is shown here. The full output can be found in the logs/chap2-ex0c.log file. At line 55 of ExCCEc.java we are invoking ExcCCECtx.useValue(Object) on the instance loaded and created in lines 37-48 using the URLClassLoader ucl1. The ExObj passed in is the one loaded and created in lines 25-35 via the URLClassLoader ucl0. The exception results when the ExCtx.useValue code attempts to cast the argument passed in to a ExObj . To understand why this fails consider the debugging output from the chap2-ex0c.log file shown in See The chap2-ex0c.log debugging output for the ExObj classes seen..
org.jboss.chap2.ex0.ExObj(113fe2).ClassLoader=java.net.URLClassLoader@6e3914
..java.net.URLClassLoader@6e3914
....file:/C:/Scott/JBoss/Books/AdminDevel/education/books/admin-devel/examples/output/chap2/j0.jar
++++CodeSource: (file:/C:/Scott/JBoss/Books/AdminDevel/education/books/admin-devel/examples/output/chap2/j0.jar <no certificates>)
++interface java.io.Serializable(7934ad)
[INFO,ExCtx] useValue2.ExObj class
org.jboss.chap2.ex0.ExObj(415de6).ClassLoader=java.net.URLClassLoader@30e280
..java.net.URLClassLoader@30e280
....file:/C:/Scott/JBoss/Books/AdminDevel/education/books/admin-devel/examples/output/chap2/j0.jar
++++CodeSource: (file:/C:/Scott/JBoss/Books/AdminDevel/education/books/admin-devel/examples/output/chap2/j0.jar <no certificates>)
++interface java.io.Serializable(7934ad)
The first output prefixed with [INFO,UCL0] shows that the ExObj class loaded at line ExCCEc.java:31 has a hash code of 113fe2 and an associated URLClassLoader instance with a hash code of 6e3914, which corresponds to ucl0. This is the class used to create the instance passed to the ExCtx.useValue method. The second output prefixed with [INFO,ExCtx] shows that the ExObj class as seen in the context of the ExCtx.useValue method has a hash code of 415de6 and a URLClassLoader instance with an associated hash code of 30e280, which corresponds to ucl1. So even though the ExObj classes are the same in terms of actual bytecode since it comes from the same j0.jar, the classes are different as seen by both the ExObj class hash codes, and the associated URLClassLoader instances. Hence, attempting to cast an instance of ExObj from one scope to the other results in the ClassCastException .
This type of error is common when one redeploys an application to which other applications are holding references to classes from the redeployed application. For example, a standalone war accessing an ejb. If you are redeploying an application, all dependent applications must flush their class references. Typically this requires that the dependent applications themselves be redeployed.
An alternate means of allowing independent deployments to interact in the presence of redeployment would be to isolate the deployments by configuring the EJB layer to use the standard call-by-value semantics rather than the call-by-reference JBoss will default to for components collocated in the same VM. An example of how to enable call-by-value semantics is presented in "Chapter 5, Enabling Call by Reference TODO: replace with real reference".
A java.lang.IllegalAccessException is thrown when one attempts to access a method or member that visibility qualifiers do not allow. Typical examples are attempting to access private or protected methods or instance variables. Another common example is accessing package protected methods or members from a class that appears to be in the correct package, but is really not due to caller and callee classes being loaded by different class loaders. An example of this is illustrated by the code shown in See Classes demonstrating the need for loading constraints..
The ExIAEd.main method uses reflection to load the ExCtx class via the ucl0 URLClassLoaders while the ExIEAd class was loaded by the application class loader. We will run this example to demonstrate how the IllegalAccessException can occur and then look at the specific issue with the example. Run the example using the following command:
[orb@toki examples]$ ant -Dchap=chap2 -Dex=0d run-example
[java] [ERROR,UCL0] Failed to invoke ExCtx.pkgUseValue
[java] java.lang.IllegalAccessException: Class org.jboss.chap2.ex0.ExIAEd can not access a member of class org.jboss.chap2.ex0.ExCtx with modifiers ""
[java] at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:57)
[java] at java.lang.reflect.Method.invoke(Method.java:317)
[java] at org.jboss.chap2.ex0.ExIAEd.main(ExIAEd.java:48)
The truncated output shown here illustrates the IllegalAccessException . The full output can be found in the logs/chap2-ex0d.log file. At line 48 of ExIAEd.java the ExCtx.pkgUseValue(Object) method is invoked via reflection. The pkgUseValue method has package protected access and even though both the invoking class ExIAEd and the ExCtx class whose method is being invoked reside in the org.jboss.chap2.ex0 package, the invocation is seen to be invalid due to the fact that the two classes are loaded by different class loaders. This can be seen by looking at the debugging output from the chap2-ex0d.log file. The key lines are:
org.jboss.chap2.ex0.ExIAEd(65855a).ClassLoader=sun.misc.Launcher$AppClassLoader@3f52a5
..sun.misc.Launcher$AppClassLoader@3f52a5
org.jboss.chap2.ex0.ExCtx(70eed6).ClassLoader=java.net.URLClassLoader@113fe2
..java.net.URLClassLoader@113fe2
The ExIAEd class is seen to have been loaded via the default application class loader instance sun.misc.Launcher$AppClassLoader@3f52a5 , while the ExCtx class was loaded by the java.net.URLClassLoader@113fe2 instance. Because the classes are loaded by different class loaders, access to the package protected method is seen to be a security violation. So, not only is type a function of both the fully qualified class name and class loader, the package scope is as well.
An example of how this can happen in practise is to include the same classes in two different SAR deployments. If classes in the deployment have a package protected relationship, users of the sar service may end up loading one class from SAR class loading at one point, and then load another class from the second SAR at a later time. If the two classes in question have a protected access relationship an IllegalAccessError will result. The solution is to either include the classes in a separate jar that is referenced by the SARs, or to combine the SARs into a single deployment. This can either be a single SAR, or an EAR the includes both SARs.
To address the type-safety problems of the early Java VMs, the notion of loading constraints were added to the 1.2 Java language spec. Loading constraints validate type expectations in the context of class loader scopes to ensure that a class X is consistently the same class when multiple class loaders are involved. This is important because Java allows for user defined class loaders LinkageErrors are essentially an extension of the ClassCastException that is enforced by the VM when classes are loaded and used.
To understand what loading constraints are and how they ensure type-safety we will first introduce the nomenclature of the Liang and Bracha paper along with an example from this paper. There are two type of class loaders, initiating and defining. An initiating class loader is one that a ClassLoader.loadClass method has been invoked on to initiate the loading of the named class. A defining class loader is the loader that calls one of the ClassLoader.defineClass methods to convert the class byte code into a Class instance. The most complete expression of a class is given by:
where C is the fully qualified class name, is the defining class loader, and is the initiating class loader. In a context where the initiating class loader is not important the type may be represented by , while when the defining class loader is not important, the type may be represented by . In the latter case, there is still a defining class loader, its just not important what the identity of the defining class loader is. Also, a type is completely defined by . The only time the initiating loader is relevant is when a loading constraint is being validated. Now consider the classes shown in See Classes demonstrating the need for loading constraints..
x.secret_value = 1; // Should not be allowed
----------------------------------------------------------------------------
----------------------------------------------------------------------------
----------------------------------------------------------------------------
The class C is defined by L 1 and so L 1 is used to initiate loading of the classes Spoofed and Delegated referenced in the C.f() method. The Spoofed class is defined by L 1 , but Delegated is defined by L 2 because L 1 delegates to L 2 . Since Delegated is defined by L 2 , L 2 will be used to initiate loading of Spoofed in the context of the Delegated.g() method. In this example both L 1 and L 2 define different versions of Spoofed as indicated by the two versions shown at the end of See Classes demonstrating the need for loading constraints.. Since C.f() believes x is an instance of it is able to access the private field scecret_value of returned by Delegated.g() due to the 1.1 and earlier Java VM's failure to take into account that a class type is determined by both the fully qualified name of the class and the defining class loader.
Java 1.2 and beyond addresses this problem by generating loader constraints to validate type consistency when the types being used are coming from different defining class loaders. For the See Classes demonstrating the need for loading constraints. example, the VM generates a constraint when the first line of method C.f() is verified to indicate that the type Spoofed must be the same regardless of whether the load of Spoofed is initiated by L 1 or L 2 . It does not matter if L 1 or L 2 , or even some other class loader defines Spoofed . All that matters is that there is only one Spoofed class defined regardless of whether L 1 or L 2 was used to initiate the loading. If L 1 or L 2 have already defined separate versions of Spoofed when this check is made a LinkageError will be generated immediately. Otherwise, the constraint will be recorded and when Delegated.g() is executed, any attempt to load a duplicate version of Spoofed will result in a LinkageError .
Now let's take a look at how a LinkageError can occur with a concrete example. See A concrete example of a LinkageError. gives the example main class along with the custom class loader used.
----------------------------------------------------------------------------
The key component in this example is the URLClassLoader subclass Ex0URLClassLoader . This class loader implementation overrides the default parent delegation model to allow the ucl0 and ucl1 instances to both load the ExObj2 class and then setup a delegation relationship from ucl0 to ucl1. At lines 28 and 29 of ExLE.main the ucl0 Ex0URLClassLoader is used to load the ExCtx and ExObj2 classes. At line 43 of ExLE.main the ucl1 Ex0URLClassLoader is used to load the ExObj2 class again. At this point both the ucl0 and ucl1 class loaders have defined the ExObj2 class. A delegation relationship from ucl0 to ucl1 is then setup at line 49 via the ucl0.setDelegate(ucl1) method call. Finally, at line 53 of ExLE.main an instance of ExCtx is created using the class loaded via ucl0. The ExCtx class is the same as presented in See The ExCtx, ExObj, and ExObj2 classes used by the examples., and the constructor was:
Now, since the ExCtx class was defined by the ucl0 class loader, and at the time the ExCtx constructor is executed, ucl0 delegates to ucl1, line 24 of the ExCtx constructor involves the following expression which has been rewritten in terms of the complete type expressions:
This generates a loading constraint of since the ExObj2 type must be consistent across the ucl0 and ucl1 class loader instances. Because we have loaded ExObj2 using both ucl0 and ucl1 prior to setting up the delegation relationship, the constraint will be violated and should generate a LinkageError when run. Run the example using the following command:
[nr@toki examples]$ ant -Dchap=chap2 -Dex=0e run-example
[java] java.lang.LinkageError: loader constraints violated when linking org/jboss/chap2/ex0/ExObj2 class
[java] at org.jboss.chap2.ex0.ExCtx.<init>(ExCtx.java:24)
[java] at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
[java] at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
[java] at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
[java] at java.lang.reflect.Constructor.newInstance(Constructor.java:274)
[java] at java.lang.Class.newInstance0(Class.java:308)
[java] at java.lang.Class.newInstance(Class.java:261)
[java] at org.jboss.chap2.ex0.ExLE.main(ExLE.java:53)
As expected, a LinkageError is thrown while validating the loader constraints required by line 24 of the ExCtx constructor.
Debugging class loading issues comes down to finding out where a class was loaded from. A useful tool for this is the code snippet shown in See Obtaining debugging information for a Class. taken from the org.jboss.util.Debug class of the book examples.
StringBuffer results = new StringBuffer();
ClassLoader cl = clazz.getClassLoader();
results.append("\n"+clazz.getName()+"("+Integer.toHexString(clazz.hashCode())+").ClassLoader="+cl);
results.append("\n.."+parent);
URL[] urls = getClassLoaderURLs(parent);
int length = urls != null ? urls.length : 0;
for(int u = 0; u < length; u ++)
results.append("\n...."+urls[u]);
if( showParentClassLoaders == false )
CodeSource clazzCS = clazz.getProtectionDomain().getCodeSource();
results.append("\n++++CodeSource: "+clazzCS);
results.append("\n++++Null CodeSource");
The key items are shown in bold. The first is that every Class object knows its defining ClassLoader and this is available via the getClassLoader() method. The defines the scope in which the Class type is known as we have just seen in the previous sections on ClassCastException s, IllegalAccessException s and LinkageError s. From the ClassLoader you can view the hierarchy of ClassLoaders that make up the parent delegation chain. If the ClassLoader is a URLClassLoader you can also see the URLs used for class and resource loading.
The defining ClassLoader of a Class cannot tell you from what location a Class was loaded. To determine this you must obtain the java.security.ProtectionDomain and then the java.security.CodeSource . It is the CodeSource that has the URL location from which the class originated. Note that not every Class has a CodeSource . If a class is loaded by the bootstrap then its CodeSource will be null. This will be the case for all classes in the java.* and javax.* packages of the VM implementation for example.
Beyond that it may be useful to view the details of classes being loaded into the JBoss server. You can enable verbose logging of the JBoss class loading layer using a Log4j configuration fragment like that shown in See An example log4j.xml configuration fragment for enabling verbose class loading logging..
<appender name="UCL" class="org.apache.log4j.FileAppender">
<param name="File" value="${jboss.server.home.dir}/log/ucl.log"/>
<param name="Append" value="false"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="[%r,%c{1},%t] %m%n"/>
<category name="org.jboss.mx.loading" additivity="false">
<priority value="TRACE" class="org.jboss.logging.XLevel"/>
This places the output from the classes in the org.jboss.mx.loading package into the ucl.log file of the server configurations log directory. Although it may not be meaningful if you have not looked at the class loading code, it is vital information needed for submitting bug reports or questions regarding class loading problems. If you have a class loading problem that appears to be a bug, submit it to the JBoss project on SourceForge and include this log file as an attachment. If the log file is too big, compress it and mail it to [email protected] .
Now that we have the role of class loaders in the Java type system defined, let's take a look at the JBoss 3.x class loading architecture. See The JBoss 3.x core class loading components. illustrates the basic components in the core of the class loading architecture.
The central component is the org.jboss.mx.loading.UnifiedClassLoader3 (UCL) class loader. This is an extension of the standard java.net.URLClassLoader that overrides the standard parent delegation model to use a shared repository of classes and resources. This shared repository is the org.jboss.mx.loading.UnifiedLoaderRepository3 . Every UCL is associated with a single UnifiedLoaderRepository3 , and a UnifiedLoaderRepository3 typically has many UCLs. As of the 3.0.5RC1 release, a UCL may have multiple URLs associated with it for class and resource loading. Prior to this release, a UCL always was associated with a single URL. This could lead to IllegalAccessExceptions and LikageErrors due to packaging issues we saw in the previous sections where we discussed how these types of errors can arise when multiple class loaders exist. With the 3.0.5RC1 release, deployers use the top-level deployment's UCL as a shared class loader and all deployment archives are assigned to this class loader. We will talk about the JBoss deployers and their interaction with the class loading system in more detail latter in the section JBoss MBean Services.
When a UCL is asked to load a class, it first looks to the repository cache it is associated with to see if the class has already been loaded. Only if the class does not exist in the repository will it be loaded into the repository by the UCL. By default, there is a single UnifiedLoaderRepository3 shared across all UCL instances. This means the UCLs form a single flat class loader namespace. The complete sequence of steps that occur when a UnfiedClassLoader3.loadClass(String, boolean) method is called is:
Another useful source of information on classes is the UnifiedLoaderRepository itself. This is an MBean that contains operations to display class and package information. The default repository is located under a standard JMX name of "JMImplementation:name=Default,service=LoaderRepository", and its MBean can be accessed via the JMX console by following its link from the front page. This hyperlink: LoaderRepository takes you to the JMX console view shown in See The default class LoaderRepository MBean view in the JMX console.. We discuss the JMX console in the next section, Connecting to the JMX Server.
Two useful operations you will find here are getPackageClassLoaders(String) and displayClassInfo(String) . The getPackageClassLoaders operation returns a set of class loaders that have been indexed to contain classes or resources for the given package name. The package name must have a trailing period. If you type in the package name "org.jboss.ejb.", the following representation is displayed:
[org.jboss.mx.loading.UnifiedClassLoader3@166a22b{ url=file:/C:/tmp/JBoss/jboss-3.0.5RC2/server/default/tmp/deploy/server/default/conf/jboss-service.xml/1.jboss-service.xml ,addedOrder=2}]
This is the string representation of the set. It shows one UnifiedClassLoader3 instance with a primary URL pointing to the default/conf/jboss-service.xml descriptor. This is the second class loader added to the repository (shown by the addedOrder=2) and it is the class loader that owns all of the jars in the lib directory of the server configuration (e.g., server/default/lib). If you enter the package name "org.jboss.jmx.adaptor.rmi.", then the following set will be displayed:
[org.jboss.mx.loading.UnifiedClassLoader3@47393f{ url=file:/C:/tmp/JBoss/jboss-3.0.5RC2/server/default/tmp/deploy/server/default/conf/jboss-service.xml/1.jboss-service.xml ,addedOrder=2}, org.jboss.mx.loading.UnifiedClassLoader3@156e5ed{ url=file:/C:/tmp/JBoss/jboss-3.0.5RC2/server/default/deploy/jmx-rmi-adaptor.sar/ ,addedOrder=6}]
This time there are two UnifiedClassLoader3 instances, one for the default/conf/jboss-service.xml and one for the default/deploy/jmx-rmi-adaptor.sar.
The view the information for a given class, use the displayClassInfo operation, passing in the fully qualified name of the class to view. For example, if we use "org.jboss.jmx.adaptor.rmi.RMIAdaptorImpl" which is from the package we just looked at, the following description is displayed:
org.jboss.jmx.adaptor.rmi.RMIAdaptorImpl Information
org.jboss.jmx.adaptor.rmi.RMIAdaptorImpl(11bd9c9).ClassLoader=org.jboss.mx.loading.UnifiedClassLoader3@166a22b{ url=file:/C:/tmp/JBoss/jboss-3.0.5RC2/server/default/tmp/deploy/server/default/conf/jboss-service.xml/1.jboss-service.xml ,addedOrder=2}
..org.jboss.mx.loading.UnifiedClassLoader3@166a22b{ url=file:/C:/tmp/JBoss/jboss-3.0.5RC2/server/default/tmp/deploy/server/default/conf/jboss-service.xml/1.jboss-service.xml ,addedOrder=2}
..org.jboss.system.server.NoAnnotationURLClassLoader@1bc4459
..sun.misc.Launcher$AppClassLoader@12f6684
....file:/C:/tmp/JBoss/jboss-3.0.5RC2/bin/
....file:/C:/usr/local/Java/j2sdk1.4.1_01/lib/tools.jar
....file:/C:/tmp/JBoss/jboss-3.0.5RC2/bin/run.jar
..sun.misc.Launcher$ExtClassLoader@f38798
....file:/C:/usr/local/Java/j2sdk1.4.1_01/jre/lib/ext/dnsns.jar
....file:/C:/usr/local/Java/j2sdk1.4.1_01/jre/lib/ext/ldapsec.jar
....file:/C:/usr/local/Java/j2sdk1.4.1_01/jre/lib/ext/localedata.jar
....file:/C:/usr/local/Java/j2sdk1.4.1_01/jre/lib/ext/sunjce_provider.jar
++++CodeSource: (file:/C:/tmp/JBoss/jboss-3.0.5RC2/server/default/lib/jboss.jar )
++interface org.jboss.jmx.adaptor.rmi.RMIAdaptor(98f192)
++++ClassLoader: org.jboss.mx.loading.UnifiedClassLoader3@e31e33{ url=file:/C:/tmp/JBoss/jboss-3.0.5RC2/server/default/deploy/jmx-rmi-adaptor.sar/ ,addedOrder=6}
++++CodeSource: (file:/C:/tmp/JBoss/jboss-3.0.5RC2/server/default/deploy/jmx-rmi-adaptor.sar/ )
### Instance0 found in UCL: org.jboss.mx.loading.UnifiedClassLoader3@166a22b{ url=file:/C:/tmp/JBoss/jboss-3.0.5RC2/server/default/tmp/deploy/server/default/conf/jboss-service.xml/1.jboss-service.xml ,addedOrder=2}
The information is a dump of the information for the Class instance in the loader repository if one has been loaded, followed by the class loaders that are seen to have the class file available. If a class is seen to have more than one class loader associated with it, then there is the potential for class loading related errors.
If you need to deploy multiple versions of an application the default 3.x class loading model would require that each application be deployed in a separate JBoss server. Sometimes this is desirable as you have more control over security and resource monitoring, but it can be difficult to manage multiple server instances. An alternative mechanism exists that allows multiple versions of an application to be deployed using deployment based scoping.
With deployment based scoping, each deployment creates its own class loader repository in the form of a HeirarchicalLoaderRepository3 that looks first to the UnifiedClassLoader3 instances of the deployment units included in the EAR before delegating to the default UnifiedLoaderRepository3 . To enable an EAR specific loader repository, you need to create a META-INF/jboss-app.xml descriptor as shown in See An example jboss-app.xml descriptor for enabled scoped class loading at the ear level..
<loader-repository>some.dot.com:loader=webtest.ear</loader-repository>
The value of the loader-repository element is the JMX ObjectName to assign to the repository created for the EAR. This must be unique and valid JMX ObjectName, but the actual name is not important.
The previous discussion of the core class loading components introduced the custom UnifiedClassLoader3 and UnifiedLoaderRepository3 classes that form a shared class loading space. The complete class loading picture must also include the parent class loader used by UnifiedClassLoader3 s as well as class loaders introduced for scoping and other speciality class loading purposes. See A complete class loader view. shows an outline of the class hierarchy that would exist for an EAR deployment containing EJBs and WARs.
The following points apply to See A complete class loader view.:
In its current form there are some advantages and disadvantages to the 3.x class loading architecture. Advantages include:
XMBeans are the JBoss JMX implementation version of the JMX ModelMBean . XMBeans have the richness of the DynamicMBean metadata without the tedious programming required by a direct implementation of the DynamicMBean interface. The JBoss Model MBean implementation allows one to specify the management interface of a component through a XML descriptor, hence the X in XMBean. In addition to providing a simple mechanism for describing the metadata required for a DynamicMBean, XMBeans also allow for the specification of attribute persistence, caching behavior, and even advanced customizations like the MBean implementation interceptors. The high level elements of the jboss_xmbean_1_0.dtd for the XMBean descriptor is given in See The JBoss 1.0 XMBean DTD Overview (jboss_xmbean_1_0.dtd).. An expanded version of the DTD is given in See An expanded view of the jboss_xmbean_1_0 DTD..
The mbean element is the root element of the document containing the required elements for describing the management interface of one MBean (constructors, attributes, operations and notifications). It also includes an optional description element, which can be used to describe the purpose of the MBean, as well as an optional descriptors element which allows for persistence policy specification, attribute caching, etc.
The descriptors element contains all the descriptors for a containing element, as subelements. The descriptors suggested in the jmx spec as well as those used by JBoss have predefined elements and attributes, whereas custom descriptors have a generic descriptor element with name and value attributes. See The descriptors element content model. shows the descriptors content model.
The key descriptors child elements include:
When specifying a custom interceptor stack you would typically include the standard interceptors along with your own unless you are replacing the corresponding standard interceptor.
Each interceptor element content value specifies the fully qualified class name of the interceptor implementation, and the class must implement the org.jboss.mx.interceptor.Interceptor interface. The interceptor class must also have either a no-arg constructor, or a constructor that accepts a ( javax.management.MBeanInfo , org.jboss.mx.server.MBeanInvoker ) pair.
The interceptor elements may have any number of attributes that correspond to JavaBean style properties on the interceptor class implementation. For each interceptor element attribute specified, the interceptor class is queried for a matching setter method. The attribute value is converted to the true type of the interceptor class property using the java.beans.PropertyEditor associated with the type. It is an error to specify an attribute for which there is no setter or PropertyEditor .
However, note that this descriptor is not currently used.
Note that any of the constructor, attribute, operation or notification elements may have a descriptors element to specify the specification defined descriptors as well as arbitrary extension descriptor settings.
The class element specifies the fully qualified name of the managed object whose management interface is described by the XMBean descriptor.
The constructor element(s) specifies the constructors available for creating an instance of the managed object. The constructor element and its content model are shown in See The XMBean constructor element and its content model..
The attribute element(s) specifies the management attributes exposed by the MBean. The attribute element and its content model are shown in See The XMBean attribute element and its content model..
The attribute element supported attributes include:
The key child elements of the attribute element include:
The management operations exposed by the XMBean are specified via one or more operation elements. The operation element and its content model are shown in See The XMBean operation element and its content model..
The impact attribute defines the impact of executing the operation and must be one of:
The notification element(s) describes the management notifications that may be emitted by the XMBean. The notification element and its content model is shown in See The XMBean notification element and content model...
For a reference of the complete DTD content model see the expanded view of the complete provided in See An expanded view of the jboss_xmbean_1_0 DTD.. We will work through examples of creating an XMBeans when we discuss the JBoss MBean services notion. See XMBean Examples. for these examples.
JBoss includes adaptors that allow access to the JMX MBeanServer from outside of the JBoss server VM. The current adaptors include HTML, an RMI interface, and an EJB.
JBoss 3.0.1 and higher comes with its own implementation of a JMX HTML adaptor that allows one to view the MBeanServer 's MBeans using a standard web browser. The default URL for the console web application is http://localhost:8080/jmx-console/ . If you browse this location you will see something similar to that presented in See The JBoss JMX console web application agent view..
The top view is called the agent view and it provides a listing of all MBeans registered with the MBeanServer sorted by the domain portion of the MBean ObjectName . Under each domain is the MBeans under that domain. When you select one of the MBeans you will be taken to the MBean view. This allows one to view and edit an MBean's attributes as well as invoke operations. As an example, See The MBean view for the "jboss.system:type=Server" MBean. shows the MBean view for the "jboss.system:type=Server" MBean.
The source code for the JMX console web application is located in the jboss-all/varia module under the src/main/org/jboss/jmx directory. Its web pages are located under jboss-all/varia/src/resources/jmx. The application is a simple MVC servlet with JSP views that utilizes the MBeanServer.
Since the JMX console web application is just a standard servlet, it may be secured using standard J2EE role based security. The jmx-console.war that ships with the 3.0.1 and latter releases is deployed as an unpacked war that includes template settings for quickly enabling simple username and password based access restrictions. If you look at the jmx-console.war in the server/default/deploy directory you will find the web.xml and jboss-web.xml descriptors in the WEB-INF directory and a roles.properties and users.properties file under WEB-INF/classes as shown here:
[nr@toki jboss-3.2.3]$ ls server/default/deploy/jmx-console.war/WEB-INF
[nr@toki jboss-3.2.3]$ ls server/default/deploy/jmx-console.war/WEB-INF/classes
org roles.properties users.properties
By uncommenting the security sections of the web.xml and jboss-web.xml descriptors as shown in See The jmx-console.war web.xml and jboss-web.xml descriptors with the security elements uncommented.., you enable HTTP basic authentication that restricts access to the jmx-console application to username=admin, password=admin. The username and password are determined by the admin=admin line in the WEB-INF/classes/users.properties file.
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<!-- A security constraint that restricts access to the HTML JMX console
to users with the role JBossAdmin. Edit the roles to what you want and
uncomment the WEB-INF/jboss-web.xml/security-domain element to enable
secured access to the HTML JMX console.
<web-resource-name>HtmlAdaptor</web-resource-name>
<description>An example security config that only allows users with the
role JBossAdmin to access the HTML JMX console web application
<http-method>GET</http-method>
<http-method>POST</http-method>
<role-name>JBossAdmin</role-name>
<auth-method>BASIC</auth-method>
<realm-name>JBoss JMX Console</realm-name>
<role-name>JBossAdmin</role-name>
<!-- Uncomment the security-domain to enable security. You will
need to edit the htmladaptor login configuration to setup the
login modules used to authentication users.
<security-domain>java:/jaas/jmx-console</security-domain>
Make these changes and either with the server running or prior to starting and then when you try to access the jmx-console URL you will see a dialog similar to that shown in See The jmx-console basic HTTP login dialog presented after making the changes shown in Listing 2-11...
Its generally a bad idea to use the properties files for securing access to the JMX console application. To see how to properly configure the security settings of web applications see Chapter 8.
JBoss supplies an RMI interface for connecting to the JMX MBeanServer. This interface is org.jboss.jmx.adaptor.rmi.RMIAdaptor , and it is shown in See The RMIAdaptor interface..
* JBoss, the OpenSource J2EE webOS
* Distributable under LGPL license.
* See terms of license at gnu.org.
package org.jboss.jmx.adaptor.rmi;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.ObjectName;
import javax.management.QueryExp;
import javax.management.ObjectInstance;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.MBeanInfo;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.InvalidAttributeValueException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanRegistrationException;
import javax.management.NotCompliantMBeanException;
import javax.management.OperationsException;
import javax.management.ReflectionException;
public ObjectInstance createMBean(String pClassName, ObjectName pName)
InstanceAlreadyExistsException,
public ObjectInstance createMBean(String pClassName, ObjectName pName,
InstanceAlreadyExistsException,
public ObjectInstance createMBean(String pClassName, ObjectName pName,
Object[] pParams, String[] pSignature)
InstanceAlreadyExistsException,
public ObjectInstance createMBean(String pClassName, ObjectName pName,
ObjectName pLoaderName, Object[] pParams, String[] pSignature)
InstanceAlreadyExistsException,
public void unregisterMBean(ObjectName pName)
throws InstanceNotFoundException,
public ObjectInstance getObjectInstance(ObjectName pName)
throws InstanceNotFoundException,
public Set queryMBeans(ObjectName pName, QueryExp pQuery)
public Set queryNames(ObjectName pName, QueryExp pQuery)
public boolean isRegistered(ObjectName pName)
public boolean isInstanceOf(ObjectName pName, String pClassName)
throws InstanceNotFoundException,
public Integer getMBeanCount()
public Object getAttribute(ObjectName pName, String pAttribute)
public AttributeList getAttributes(ObjectName pName, String[] pAttributes)
throws InstanceNotFoundException,
public void setAttribute(ObjectName pName, Attribute pAttribute)
throws InstanceNotFoundException,
InvalidAttributeValueException,
public AttributeList setAttributes(ObjectName pName, AttributeList pAttributes)
throws InstanceNotFoundException,
public Object invoke(ObjectName pName, String pActionName,
Object[] pParams, String[] pSignature)
throws InstanceNotFoundException,
public String getDefaultDomain()
public void addNotificationListener(ObjectName pName, ObjectName pListener,
NotificationFilter pFilter, Object pHandback)
throws InstanceNotFoundException,
public void removeNotificationListener(ObjectName pName, ObjectName pListener)
throws InstanceNotFoundException,
public MBeanInfo getMBeanInfo(ObjectName pName)
throws InstanceNotFoundException,
The RMIAdaptor interface was bound into JNDI by the org.jboss.jmx.adaptor.rmi.RMIAdaptorService MBean, but as of 3.2.2 this service has been removed from the dist deploy directory by default. It can still be found in the docs/examples/jmx directory, but it has been deprecated in favor of the invoker adaptor service. This service also supports the RMIAdaptor interface and its configuration also provides a binding of this interface in the default location of "jmx/rmi/RMIAdaptor" for backwards compatibility with existing clients. For the details of the invoker adaptor service, see Figure , A Detached Invoker Example, the MBeanServer Invoker Adaptor Service. The RMIAdaptorService still has utility for remote clients that need to receive JMX notifications. The invoker adaptor service does not yet suppor this capability so if this is required, the jmx-invoker-adaptor-server.sar must be replaced with the jmx-rmi-adaptor.sar from the examples directory.
The RMIAdaptorService is deployed as the jmx-rmi-adaptor.sar package, and supports the following attributes:
See A JMX client that uses the RMIAdaptor. shows a client that makes use of the RMIAdaptor interface to query the MBeanInfo for the JNDIView MBean. It also invokes the MBean's list(boolean) method and displays the result.
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.ObjectName;
import javax.naming.InitialContext;
import org.jboss.jmx.adaptor.rmi.RMIAdaptor;
/** A client that demonstrates how to connect to the JMX server using the RMI
@author [email protected]
* @param args the command line arguments
public static void main(String[] args) throws Exception
InitialContext ic = new InitialContext();
RMIAdaptor server = (RMIAdaptor) ic.lookup("jmx/rmi/RMIAdaptor");
// Get the MBeanInfo for the JNDIView MBean
ObjectName name = new ObjectName("jboss:service=JNDIView");
MBeanInfo info = server.getMBeanInfo(name);
System.out.println("JNDIView Class: "+info.getClassName());
MBeanOperationInfo[] opInfo = info.getOperations();
System.out.println("JNDIView Operations: ");
for(int o = 0; o < opInfo.length; o ++)
MBeanOperationInfo op = opInfo[o];
String returnType = op.getReturnType();
System.out.print(" + "+returnType+" "+opName+"(");
MBeanParameterInfo[] params = op.getSignature();
for(int p = 0; p < params.length; p ++)
MBeanParameterInfo paramInfo = params[p];
String pname = paramInfo.getName();
String type = paramInfo.getType();
System.out.print(type+" "+name);
// Invoke the list(boolean) op
Object[] opArgs = {Boolean.TRUE};
Object result = server.invoke(name, "list", opArgs, sig);
System.out.println("JNDIView.list(true) output:\n"+result);
To test the client access using the RMIAdaptor , run the following:
[orb@toki examples]$ ant -Dchap=chap2 -Dex=4 run-example
[java] ImplementationTitle: JBoss [WonderLand]
[java] ImplementationVendor: JBoss.org
[java] ImplementationVersion: 3.2.3 (build: CVSTag=JBoss_3_2_3 date=200311301445)
[java] SpecificationTitle: JBoss
[java] SpecificationVendor: JBoss (http://www.jboss.org/)
[java] SpecificationVersion: 3.2.3
[java] JBoss version is: 3.2.3
[echo] Using jboss.dist=/Users/orb/java/jboss-3.2.3
[javac] Compiling 3 source files to /Users/orb/Desktop/jboss/AdminDevel 2/examples/output/classes
[java] JNDIView Class: org.jboss.mx.modelmbean.XMBean
[java] + java.lang.String list(boolean jboss:service=JNDIView)
[java] + java.lang.String listXML()
[java] + java.lang.String getName()
[java] + java.lang.String getStateString()
[java] JNDIView.list(true) output:
[java] <h1>Ejb Module: ClusteredHttpSessionEB.jar</h1>
[java] <h2>java:comp namespace of the ClusteredHTTPSession bean:</h2>
[java] +- env (class: org.jnp.interfaces.NamingContext)
[java] <h1>java: Namespace</h1>
[java] +- jaas (class: javax.naming.Context)
[java] | +- jbossmq-httpil (class: org.jboss.security.plugins.SecurityDomainContext)
[java] | +- other (class: org.jboss.security.plugins.SecurityDomainContext)
[java] | +- JmsXARealm (class: org.jboss.security.plugins.SecurityDomainContext)
[java] | +- jbosstest-web (class: org.jboss.security.plugins.SecurityDomainContext)
[java] | +- http-invoker (class: org.jboss.security.plugins.SecurityDomainContext)
[java] | +- jbossmq (class: org.jboss.security.plugins.SecurityDomainContext)
[java] | +- secure-jndi (class: org.jboss.security.plugins.SecurityDomainContext)
[java] | +- HsqlDbRealm (class: org.jboss.security.plugins.SecurityDomainContext)
[java] +- TransactionPropagationContextImporter (class: org.jboss.tm.TransactionPropagationContextImporter)
[java] +- JmsXA (class: org.jboss.resource.adapter.jms.JmsConnectionFactoryImpl)
[java] +- JBossCorbaNaming (class: org.omg.CosNaming.NamingContextExt)
[java] +- DefaultDS (class: org.jboss.resource.adapter.jdbc.WrapperDataSource)
[java] +- StdJMSPool (class: org.jboss.jms.asf.StdServerSessionPoolFactory)
[java] +- TransactionManager (class: org.jboss.tm.TxManager)
[java] +- JBossCorbaPOA (class: org.omg.PortableServer.POA)
[java] +- TransactionPropagationContextExporter (class: org.jboss.tm.TransactionPropagationContextFactory)
[java] +- ConnectionFactory (class: org.jboss.mq.SpyConnectionFactory)
[java] +- DefaultJMSProvider (class: org.jboss.jms.jndi.JBossMQProvider)
[java] +- XAConnectionFactory (class: org.jboss.mq.SpyXAConnectionFactory)
[java] +- JBossCorbaInterfaceRepositoryPOA (class: org.omg.PortableServer.POA)
[java] +- Mail (class: javax.mail.Session)
[java] +- JBossCorbaORB (class: org.omg.CORBA.ORB)
[java] +- cmrTransactionTest (class: org.jnp.interfaces.NamingContext)
[java] +- timedCacheFactory (class: javax.naming.Context)
[java] Failed to lookup: timedCacheFactory, errmsg=null
[java] +- SecurityProxyFactory (class: org.jboss.security.SubjectSecurityProxyFactory)
[java] +- comp (class: javax.naming.Context)
[java] <h1>Global JNDI Namespace</h1>
[java] +- OIL2ConnectionFactory (class: org.jboss.mq.SpyConnectionFactory)
[java] +- hello (class: org.jnp.interfaces.NamingContext)
[java] +- HAPartition (class: org.jnp.interfaces.NamingContext)
[java] | +- DefaultPartition (class: org.jboss.ha.framework.server.HAPartitionImpl)
[java] +- helloworld (class: org.jnp.interfaces.NamingContext)
[java] +- jbosstest (class: org.jnp.interfaces.NamingContext)
[java] | +- ejbs (class: org.jnp.interfaces.NamingContext)
[java] | | +- local (class: org.jnp.interfaces.NamingContext)
[java] +- XAConnectionFactory (class: org.jboss.mq.SpyXAConnectionFactory)
[java] +- queue (class: org.jnp.interfaces.NamingContext)
[java] | +- D (class: org.jboss.mq.SpyQueue)
[java] | +- C (class: org.jboss.mq.SpyQueue)
[java] | +- B (class: org.jboss.mq.SpyQueue)
[java] | +- A (class: org.jboss.mq.SpyQueue)
[java] | +- testQueue (class: org.jboss.mq.SpyQueue)
[java] | +- ex (class: org.jboss.mq.SpyQueue)
[java] | +- testObjectMessage (class: org.jboss.mq.SpyQueue)
[java] | +- DLQ (class: org.jboss.mq.SpyQueue)
[java] +- test (class: org.jnp.interfaces.NamingContext)
[java] | +- entity (class: org.jnp.interfaces.NamingContext)
[java] +- RMIConnectionFactory (class: org.jboss.mq.SpyConnectionFactory)
[java] +- exception (class: org.jnp.interfaces.NamingContext)
[java] +- UUIDKeyGeneratorFactory (class: org.jboss.ejb.plugins.keygenerator.uuid.UUIDKeyGeneratorFactory)
[java] +- testTCF[link -> ConnectionFactory] (class: javax.naming.LinkRef)
[java] +- ENCTests (class: org.jnp.interfaces.NamingContext)
[java] | +- ejbs (class: org.jnp.interfaces.NamingContext)
[java] +- idgen (class: org.jnp.interfaces.NamingContext)
[java] +- ejbcts2 (class: org.jnp.interfaces.NamingContext)
[java] +- commerce (class: org.jnp.interfaces.NamingContext)
[java] +- UIL2ConnectionFactory (class: org.jboss.mq.SpyConnectionFactory)
[java] +- UserTransactionSessionFactory (proxy: $Proxy10 implements interface org.jboss.tm.usertx.interfaces.UserTransactionSessionFactory)
[java] +- UILXAConnectionFactory[link -> UIL2XAConnectionFactory] (class: javax.naming.LinkRef)
[java] +- relation (class: org.jnp.interfaces.NamingContext)
[java] | +- manyToMany (class: org.jnp.interfaces.NamingContext)
[java] | | +- bidirectional (class: org.jnp.interfaces.NamingContext)
[java] | | +- unidirectional (class: org.jnp.interfaces.NamingContext)
[java] | +- manyToOne (class: org.jnp.interfaces.NamingContext)
[java] | | +- unidirectional (class: org.jnp.interfaces.NamingContext)
[java] | | | +- table (class: org.jnp.interfaces.NamingContext)
[java] | | | +- fk (class: org.jnp.interfaces.NamingContext)
[java] | +- oneToMany (class: org.jnp.interfaces.NamingContext)
[java] | | +- bidirectional (class: org.jnp.interfaces.NamingContext)
[java] | | | +- table (class: org.jnp.interfaces.NamingContext)
[java] | | | +- fk (class: org.jnp.interfaces.NamingContext)
[java] | | +- unidirectional (class: org.jnp.interfaces.NamingContext)
[java] | | | +- table (class: org.jnp.interfaces.NamingContext)
[java] | | | +- fk (class: org.jnp.interfaces.NamingContext)
[java] | +- oneToOne (class: org.jnp.interfaces.NamingContext)
[java] | | +- bidirectional (class: org.jnp.interfaces.NamingContext)
[java] | | | +- table (class: org.jnp.interfaces.NamingContext)
[java] | | | +- fk (class: org.jnp.interfaces.NamingContext)
[java] | | +- unidirectional (class: org.jnp.interfaces.NamingContext)
[java] | | | +- table (class: org.jnp.interfaces.NamingContext)
[java] | | | +- fk (class: org.jnp.interfaces.NamingContext)
[java] +- console (class: org.jnp.interfaces.NamingContext)
[java] | +- PluginManager (proxy: $Proxy26 implements interface org.jboss.console.manager.PluginManagerMBean)
[java] +- HTTPXAConnectionFactory (class: org.jboss.mq.SpyXAConnectionFactory)
[java] +- topic (class: org.jnp.interfaces.NamingContext)
[java] | +- testDurableTopic (class: org.jboss.mq.SpyTopic)
[java] | +- testTopic (class: org.jboss.mq.SpyTopic)
[java] | +- securedTopic (class: org.jboss.mq.SpyTopic)
[java] +- testQCF[link -> ConnectionFactory] (class: javax.naming.LinkRef)
[java] +- HAILXAConnectionFactory (class: org.jboss.mq.SpyXAConnectionFactory)
[java] +- ejb (class: org.jnp.interfaces.NamingContext)
[java] | +- local (class: org.jnp.interfaces.NamingContext)
[java] | +- jca (class: org.jnp.interfaces.NamingContext)
[java] | +- remote (class: org.jnp.interfaces.NamingContext)
[java] +- cmrTransactionTest (class: org.jnp.interfaces.NamingContext)
[java] +- UserTransaction (class: org.jboss.tm.usertx.client.ClientUserTransaction)
[java] +- HTTPConnectionFactory (class: org.jboss.mq.SpyConnectionFactory)
[java] +- arrays (class: org.jnp.interfaces.NamingContext)
[java] +- RMIXAConnectionFactory (class: org.jboss.mq.SpyXAConnectionFactory)
[java] +- HAILConnectionFactory (class: org.jboss.mq.SpyConnectionFactory)
[java] +- anotherContext (class: org.jnp.interfaces.NamingContext)
[java] | +- TopicInADifferentContext[link -> topic/myMDBTopic] (class: javax.naming.LinkRef)
[java] | +- QueueInADifferentContext[link -> queue/myMDBQueue] (class: javax.naming.LinkRef)
[java] +- psuedo-url: (class: org.jnp.interfaces.NamingContext)
[java] | +- ejb (class: org.jnp.interfaces.NamingContext)
[java] +- local (class: org.jnp.interfaces.NamingContext)
[java] +- eardeployment (class: org.jnp.interfaces.NamingContext)
[java] +- OIL2XAConnectionFactory (class: org.jboss.mq.SpyXAConnectionFactory)
[java] +- HASessionState (class: org.jnp.interfaces.NamingContext)
[java] | +- Default (class: org.jboss.ha.hasessionstate.server.HASessionStateImpl)
[java] +- ejbcts (class: org.jnp.interfaces.NamingContext)
[java] +- UIL2XAConnectionFactory (class: org.jboss.mq.SpyXAConnectionFactory)
[java] +- naming (class: org.jnp.interfaces.NamingContext)
[java] | +- local (class: org.jnp.interfaces.NamingContext)
[java] +- invokers (class: org.jnp.interfaces.NamingContext)
[java] | +- toki.local (class: org.jnp.interfaces.NamingContext)
[java] | | +- iiop (class: org.jboss.invocation.iiop.IIOPInvoker)
[java] | | +- http (class: org.jboss.invocation.http.interfaces.HttpInvokerProxy)
[java] | +- 0.0.0.0 (class: org.jnp.interfaces.NamingContext)
[java] | | +- pooled (class: org.jboss.invocation.pooled.interfaces.PooledInvokerProxy)
[java] +- UILConnectionFactory[link -> UIL2ConnectionFactory] (class: javax.naming.LinkRef)
[java] +- jmx (class: org.jnp.interfaces.NamingContext)
[java] | +- invoker (class: org.jnp.interfaces.NamingContext)
[java] | | +- RMIAdaptor (proxy: $Proxy25 implements interface org.jboss.jmx.adaptor.rmi.RMIAdaptor)
[java] | +- rmi (class: org.jnp.interfaces.NamingContext)
[java] | | +- RMIAdaptor[link -> jmx/invoker/RMIAdaptor] (class: javax.naming.LinkRef)
[java] +- clustering (class: org.jnp.interfaces.NamingContext)
[java] | +- HTTPSession (proxy: $Proxy31 implements interface org.jboss.ha.httpsession.beanimpl.interfaces.ClusteredHTTPSessionHome,interface javax.ejb.Handle)
[java] | +- LocalHTTPSession (proxy: $Proxy28 implements interface org.jboss.ha.httpsession.beanimpl.interfaces.LocalClusteredHTTPSessionHome)
[java] +- ConnectionFactory (class: org.jboss.mq.SpyConnectionFactory)
[java] +- cmp2 (class: org.jnp.interfaces.NamingContext)
[java] | +- perf (class: org.jnp.interfaces.NamingContext)
[java] | +- audit (class: org.jnp.interfaces.NamingContext)
[java] | +- readonly (class: org.jnp.interfaces.NamingContext)
[java] | +- simple (class: org.jnp.interfaces.NamingContext)
[java] | +- lob (class: org.jnp.interfaces.NamingContext)
[java] +- v2 (class: org.jnp.interfaces.NamingContext)
[java] | +- local (class: org.jnp.interfaces.NamingContext)
[java] +- v1 (class: org.jnp.interfaces.NamingContext)
As of JBoss-3.2.1 there is an initial version of a command line tool that allows for simple interaction with a remote JMX server instance. This tool is called twiddle (for twiddling bits via JMX) and is located in the bin directory of the distribution. Twiddle is a command execution tool, not a general command shell. It is run using either the twiddle.sh or twiddle.bat scripts, and passing in a -h(--help) argument provides the basic syntax, and --help-commands shows what you can do with the tool:
[nr@toki bin]$ ./twiddle.sh -h
A JMX client to 'twiddle' with a remote JBoss server.
usage: twiddle.sh [options] <command> [command_arguments]
-h, --help Show this help message
--help-commands Show a list of commands
-H=<command> Show command specific help
-c=command.properties Specify the command.properties file to use
-D<name>[=<value>] Set a system property
-s, --server=<url> The JNDI URL of the remote server
-a, --adapter=<name> The JNDI name of the RMI adapter to use
[nr@toki bin]$ ./twiddle.sh --help-commands
get Get the values of one or more MBean attributes
invoke Invoke an operation on an MBean
unregister Unregister one or more MBeans
serverinfo Get information about the MBean server
query Query the server for a list of matching MBeans
info Get the metadata for an MBean
In 3.2.3, you will need to add jbossall-client.jar to your JBOSS_CLASSPATH to connect to a JBoss server over RMI using twiddle . You may see the following error:
[nr@toki bin]$ ./twiddle.sh serverinfo --list
twiddle.sh: org.jboss.util.NestedRuntimeException: - nested throwable: (javax.naming.CommunicationException [Root exception is java.lang.ClassNotFoundException: org.jboss.proxy.ClientContainer (no security manager: RMI class loader disabled)])
You can adjust the twiddle script to add this jar or adjust your JBOSS_CLASSPATH on the command line.
[nr@toki bin]$ export JBOSS_CLASSPATH=../client.jbossall-client.jar
By default the twiddle command will connect to the localhost at port 1099 to lookup the default "jmx/rmi/RMIAdaptor" binding of the RMIAdaptor service as the connector for communicating with the JMX server. To connect to a different server/port combination you can use the -s(--server) option:
[nr@rubik bin]$ ./twiddle.sh -s toki serverinfo -d
[nr@rubik bin]$ ./twiddle.sh -s toki:1099 serverinfo -d
To connect using a different RMIAdaptor binding use the -a(--adapter) option:
[nr@rubik bin]$ ./twiddle.sh -s toki -a jmx/rmi/RMIAdaptor serverinfo -d
[nr@rubik bin]$ ./twiddle.sh -s toki --adapter=jmx/rmi/RMIAdaptor serverinfo -d
To access basic information about a server, use the serverinfo command. This currently supports:
[nr@toki bin]$ ./twiddle.sh -H serverinfo
Get information about the MBean server
-d, --domain Get the default domain
-c, --count Get the MBean count
[nr@rubik bin]$ ./twiddle.sh --server=toki serverinfo --count
[nr@rubik bin]$ ./twiddle.sh --server=toki serverinfo --domain
To query the server for the name of MBeans matching a pattern, use the query command. This currently supports:
[nr@rubik bin]$ ./twiddle.sh -H query
Query the server for a list of matching MBeans
usage: query [options] <query>
-c, --count Display the matching MBean count
query all mbeans in the jboss.j2ee domain: query 'jboss.j2ee:*'
[nr@rubik bin]$ ./twiddle.sh -s toki query 'jboss:service=invoker,*'
jboss:readonly=true,service=invoker,target=Naming,type=http
jboss:service=invoker,type=jrmp
jboss:service=invoker,type=httpHA
jboss:service=invoker,type=jrmpha
jboss:service=invoker,type=local
jboss:service=invoker,type=pooled
jboss:service=invoker,type=iiop
jboss:service=invoker,type=http
jboss:service=invoker,target=Naming,type=http
To get the attributes of an MBean, use the get command:
[nr@toki bin]$ ./twiddle.sh get jboss:service=invoker,type=jrmp
Get the values of one or more MBean attributes
usage: get [options] <name> [<attr>+]
If no attribute names are given all readable attributes are gotten
--noprefix Do not display attribute name prefixes
[orb@toki bin]$ ./twiddle.sh get jboss:service=invoker,type=jrmp RMIObjectPort StateString
[nr@toki bin]$ ./twiddle.sh get jboss:service=invoker,type=jrmp
To query the MBeanInfo for an MBean, use the info command:
[nr@toki bin]$ ./twiddle.sh -H info
Use '*' to query for all attributes
[nr@toki bin]$ ./twiddle.sh info jboss:service=invoker,type=jrmp
To invoke an operation on an MBean, use the invoker command:
[nr@toki bin]$ ./twiddle.sh -H invoke
Invoke an operation on an MBean
usage: invoke [options] <query> <operation> (<arg>)*
-q, --query-type[=<type>] Treat object name as a query
f[irst] Only invoke on the first matching name [default]
a[ll] Invoke on all matching names
[nr@toki bin]$ ./twiddle.sh invoke jboss:service=JNDIView list true
<h1>Ejb Module: ClusteredHttpSessionEB.jar</h1>
<h2>java:comp namespace of the ClusteredHTTPSession bean:</h2>
+- env (class: org.jnp.interfaces.NamingContext)
+- jaas (class: javax.naming.Context)
| +- jbossmq-httpil (class: org.jboss.security.plugins.SecurityDomainContext)
| +- other (class: org.jboss.security.plugins.SecurityDomainContext)
| +- JmsXARealm (class: org.jboss.security.plugins.SecurityDomainContext)
| +- jbosstest-web (class: org.jboss.security.plugins.SecurityDomainContext)
| +- http-invoker (class: org.jboss.security.plugins.SecurityDomainContext)
| +- jbossmq (class: org.jboss.security.plugins.SecurityDomainContext)
| +- secure-jndi (class: org.jboss.security.plugins.SecurityDomainContext)
| +- HsqlDbRealm (class: org.jboss.security.plugins.SecurityDomainContext)
+- TransactionPropagationContextImporter (class: org.jboss.tm.TransactionPropagationContextImporter)
+- JmsXA (class: org.jboss.resource.adapter.jms.JmsConnectionFactoryImpl)
+- JBossCorbaNaming (class: org.omg.CosNaming.NamingContextExt)
+- DefaultDS (class: org.jboss.resource.adapter.jdbc.WrapperDataSource)
With the detached invokers and a somewhat generalized proxy factory capability, you can really talk to the JMX server using the InvokerAdaptorService and a proxy factory service to expose an RMIAdaptor or similar interface over your protocol of choice. We will introduce the detached invoker notion along with proxy factories in section Remote Access to Services, Detached Invokers. See A Detached Invoker Example, the MBeanServer Invoker Adaptor Service for an example of an invoker service that allows one to access the MBeanServer using to the RMIAdaptor interface over any protocol for which a proxy factory service exists.
When JBoss starts up, one of the first steps performed is to create an MBean server instance ( javax.management.MBeanServer ). The JMX MBean server in the JBoss architecture plays the role of a microkernel. All other manageable MBean components are plugged into JBoss by registering with the MBean server. The kernel in that sense is only an framework, and not a source of actual functionality. The functionality is provided by MBeans, and in fact all major JBoss components are manageable MBeans interconnected through the MBean server.
In this section we will describe the JBoss server startup process. A summary of the steps that occur during the JBoss server startup sequence is:
The JBoss server starts out as nothing more than a container for the JMX MBean server, and then loads its personality based on the services defined in the jboss-service.xml MBean configuration file from the named configuration set passed to the server on the command line. Because MBeans define the functionality of a JBoss server instance, it is important to understand how the core JBoss MBeans are written, and how you should integrate your existing services into JBoss using MBeans. This is the topic of the next section.
As we have seen, JBoss relies on JMX to load in the MBean services that make up a given server instance's personality. All of the bundled functionality provided with the standard JBoss distribution is based on MBeans. The best way to add services to the JBoss server is to write your own JMX MBeans.
There are two classes of MBeans: those that are independent of JBoss services, and those that are dependent on JBoss services. MBeans that are independent of JBoss services are the trivial case. They can be written per the JMX specification and added to a JBoss server by adding an mbean tag to the deploy/user-service.xml file. Writing an MBean that relies on a JBoss service such as naming requires you to follow the JBoss service pattern. The JBoss MBean service pattern consists of a set of life cycle operations that provide state change notifications. The notifications inform an MBean service when it can create, start, stop, and destroy itself. The management of the MBean service life cycle is the responsibility of three JBoss MBeans, SARDeployer , ServiceConfigurator and ServiceController .
JBoss manages the deployment of its MBean services via a custom MBean that loads an XML variation of the standard JMX MLet configuration file. This custom MBean is implemented in the org.jboss.deployment.SARDeployer class. The SARDeployer MBean is loaded when JBoss starts up as part of the bootstrap process. The SAR acronym stands for service archive.
The SARDeployer handles services archives. A service archive can be either a jar that ends with a ".sar" suffix and contains a META-INF/jboss-service.xml descriptor, or a standalone XML descriptor with a naming pattern that matches "*-service.xml". The DTD for the service descriptor is given in See the DTD for the MBean service descriptor parsed by the SARDeployer...
As of the 3.0.5 release, the text value of an attribute may reference a system property "x" by using the pattern "${x}". In this case the value of the attribute will be the result of System.getProperty("x") , or null if no such property exists.
When the SARDeployer is asked to deploy a service performs several steps. See A sequence diagram highlighting the main activities performed by the SARDeployer to start a JBoss MBean service.. is a sequence diagram that shows the init through start phases of a service.
The JMX specification does not define any type of life cycle or dependency management for MBeans. The JBoss ServiceController MBean introduces this notion. A JBoss MBean is an extension of the JMX MBean in that an MBean is expected to decouple creation from the life cycle of its service duties. This is necessary to implement any type of dependency management. For example, if you are writing an MBean that needs a JNDI naming service to be able to function, your MBean needs to be told when its dependencies are satisfied. This ranges from difficult to impossible to do if the only life cycle event is the MBean constructor. Therefore, JBoss introduces a service life cycle interface that describes the events a service can use to manage its behavior. See The org.jboss.system.Service interface. shows the org.jboss.system.Service interface:
public void create() throws Exception;
public void start() throws Exception;
The ServiceController MBean invokes the methods of the Service interface at the appropriate times of the service life cycle. We'll discuss the methods in more detail in the ServiceController section.
Note that there is a J2EE management specification request (JSR 77, http://jcp.org/jsr/detail/77.jsp) that introduces a state management notion that includes a start/stop lifecycle notion. When this standard is finalized JBoss will likely support an extension of the JSR 77 based service lifecycle implementation. As of the 3.2.0 release we do support JSR77 management objects and most of the statistics, but the lifecycle operations are not supported.
JBoss manages dependencies between MBeans via the org.jboss.system.ServiceController custom MBean. The SARDeployer delegates to the ServiceController when initializing, creating, starting, stopping and destroying MBean services. See The interaction between the SARDeployer and ServiceController to start a service.. shows a sequence diagram that highlights interaction between the SARDeployer and ServiceController .
The ServiceController MBean has four key methods for the management of the service life cycle: create, start, stop and destroy.
The create(ObjectName) method is called whenever an event occurs that affects the named services state. This could be triggered by an explicit invocation by the SARDeployer , a notification of a new Class, or another service reaching its created state.
When a service's create method is called, all services on which the service depends have also had their create method invoked. This gives an MBean an opportunity to check that required MBeans or resources exist. A service cannot utilize other MBean services at this point, as most JBoss MBean services do not become fully functional until they have been started via their start method. Because of this, service implementations often do not implement create in favor of just the start method because that is the first point at which the service can be fully functional.
The start(ObjectName) method is called whenever an event occurs that affects the named services state. This could be triggered by an explicit invocation by the SARDeployer , a notification of a new Class , or another service reaching its started state.
When a service's start method is called, all services on which the service depends have also had their start method invoked. Receipt of a start method invocation signals a service to become fully operational since all services upon which the service depends have been created and started.
The stop(ObjectName) method is called whenever an event occurs that affects the named services state. This could be triggered by an explicit invocation by the SARDeployer , notification of a Class removal, or a service on which other services depend reaching its stopped state.
The destroy(ObjectName) method is called whenever an event occurs that affects the named services state. This could be triggered by an explicit invocation by the SARDeployer , notification of a Class removal, or a service on which other services depend reaching its destroyed state.
Service implementations often do not implement destroy in favor of simply implementing the stop method, or neither stop nor destroy if the service has no state or resources that need cleanup.
To specify that an MBean service depends on other MBean services you need to declare the dependencies in the mbean element of the service descriptor. This is done using the depends and depends-list elements. One difference between the two elements relates to the optional-attribute-name attribute usage. If you track the ObjectName s of dependencies using single valued attributes you should use the depends element. If you track the ObjectName s of dependencies using java.util.List compatible attributes you would use the depends-list element. If you only want to specify a dependency and don't care to have the associated service ObjectName bound to an attribute of your MBean then use whatever element is easiest. See Service descriptor fragments illustrating the usage of the depends and depends-list elements.. shows example service descriptor fragments that illustrate the usage of the dependency related elements.
<mbean code="org.jboss.mq.server.jmx.Topic"
name="jms.topic:service=Topic,name=testTopic">
<!-- Declare a dependency on the "jboss.mq:service=DestinationManager" and
bind this name to the DestinationManager attribute -->
<depends optional-attribute-name="DestinationManager">
jboss.mq:service=DestinationManager
<!-- Declare a dependency on the "jboss.mq:service=SecurityManager" and
bind this name to the SecurityManager attribute -->
<depends optional-attribute-name="SecurityManager">
jboss.mq:service=SecurityManager
<!-- Declare a dependency on the "jboss.mq:service=CacheManager" without
any binding of the name to an attribute-->
<depends>jboss.mq:service=CacheManager</depends>
<mbean code="org.jboss.mq.server.jmx.TopicMgr"
name="jboss.mq.destination:service=TopicMgr">
<!-- Declare a dependency on the given topic destination mbeans and
bind these names to the Topics attribute -->
<depends-list optional-attribute-name="Topics">
<depends-list-element>jms.topic:service=Topic,name=A</depends-list-element>
<depends-list-element>jms.topic:service=Topic,name=B</depends-list-element>
<depends-list-element>jms.topic:service=Topic,name=C</depends-list-element>
Another difference between the depends and depends-list elements is that the value of the depends element may be a complete MBean service configuration rather than just the ObjectName of the service. See An example of using the depends element to specify the complete configuration of a depended on service.. shows an example from the hsqldb-service.xml descriptor. In this listing the org.jboss.resource.connectionmanager.RARDeployment service configuration is defined using a nested mbean element as the depends element value. This indicates that the org.jboss.resource.connectionmanager.LocalTxConnectionManager MBean depends on this service. The "jboss.jca:service=LocalTxDS,name=hsqldbDS" ObjectName will be bound to the ManagedConnectionFactoryName attribute of the LocalTxConnectionManager class.
<mbean code="org.jboss.resource.connectionmanager.LocalTxConnectionManager"
name="jboss.jca:service=LocalTxCM,name=hsqldbDS">
<depends optional-attribute-name="ManagedConnectionFactoryName">
<mbean code="org.jboss.resource.connectionmanager.RARDeployment"
name="jboss.jca:service=LocalTxDS,name=hsqldbDS">
<attribute name="JndiName">DefaultDS</attribute>
<attribute name="ManagedConnectionFactoryProperties">
<config-property name="ConnectionURL" type="java.lang.String">
jdbc:hsqldb:hsql://localhost:1476
<config-property name="DriverClass" type="java.lang.String">
<config-property name="UserName" type="java.lang.String">sa
The ServiceController MBean supports two operations that help with debugging what MBeans are not running due to unsatisfied dependencies. The first operation is listIncompletelyDeployed . This returns a java.util.List of org.jboss.system.ServiceContext objects for the MBean services that are not in the RUNNING state.
The second operation is listWaitingMBeans . This operation returns a java.util.List of the JMX ObjectName s of MBean services that cannot be deployed because the class specified by the code attribute is not available.
The URLDeploymentScanner MBean service provides the JBoss hot deployment capability. This service watches one or more URLs for deployable archives and deploys the archives as they appear or change. It also undeploys previously deployed applications if the archive from which the application was deployed is removed. The configurable attributes include:
The default value for the URLs attribute is "deploy/" which means that any SARs, EARs, JARs, WARs, RARs, etc. dropped into the server/<name>/deploy directory will be automatically deployed and watched for updates.
An an alternate implementation is the org.jboss.deployment.scanner.PrefixDeploymentSorter class. This orders the URLs based on numeric prefixes. The prefix digits are converted to an int (ignoring leading zeroes), smaller prefixes are ordered ahead of larger numbers. Deployments that do not start with any digits will be deployed after all numbered deployments. Deployments with the same prefix value are further sorted by the DeploymentSorter logic.
Writing a custom MBean service that integrates into the JBoss server requires the use of the org.jboss.system.Service interface pattern if the custom service is dependent on other services. When a custom MBean depends on other MBean services you cannot perform any service dependent initialization in any of the javax.management.MBeanRegistration interface methods since JMX has no dependency notion. Instead, you must manage dependency state using the Service interface create and/or start methods. You can do this using any one of the following approaches:
Which approach you choose depends on if you want to be associated with JBoss specific code. If you don't, then you would use the first approach. If you don't care about dependencies on JBoss classes, the simplest approach is to have your MBean interface extend from org.jboss.system.ServiceMBean and your MBean implementation class extend from the abstract org.jboss.system.ServiceMBeanSupport class. This class implements the org.jboss.system.ServiceMBean interface. ServiceMBeanSupport provides implementations of the create , start , stop , and destroy methods that integrate logging and JBoss service state management tracking. Each method delegates any subclass specific work to createService , startService , stopService , and destroyService methods respectively. When subclassing ServiceMBeanSupport , you would override one or more of the createService , startService , stopService , and destroyService methods as required
This section develops a simple MBean that binds a HashMap into the JBoss JNDI namespace at a location determined by its JndiName attribute to demonstrate what is required to create a custom MBean. Because the MBean uses JNDI, it depends on the JBoss naming service MBean and must use the JBoss MBean service pattern to be notified when the naming service is available.
The MBean you develop is called JNDIMap . Version one of the JNDIMapMBean interface and JNDIMap implementation class, which is based on the service interface method pattern, is given in See JNDIMapMBean interface and implementation based on the service interface method pattern.. This version of the interface makes use of the first approach in that it incorporates the Service interface methods needed to start up correctly, but does not do so by using a JBoss-specific interface. The interface includes the Service.start method, which will be informed when all required services have been started, and the stop method, which will clean up the service.
// The JNDIMap MBean interface
import javax.naming.NamingException;
public void setJndiName(String jndiName) throws NamingException;
public void start() throws Exception;
public void stop() throws Exception;
// The JNDIMap MBean implementation
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.jboss.naming.NonSerializableFactory;
public class JNDIMap implements JNDIMapMBean
private HashMap contextMap = new HashMap();
public void setJndiName(String jndiName) throws NamingException
String oldName = this.jndiName;
NamingException ne = new NamingException("Failed to update jndiName");
public void start() throws Exception
private void rebind() throws NamingException
InitialContext rootCtx = new InitialContext();
Name fullName = rootCtx.getNameParser("").parse(jndiName);
System.out.println("fullName="+fullName);
NonSerializableFactory.rebind(fullName, contextMap, true);
private void unbind(String jndiName)
InitialContext rootCtx = new InitialContext();
NonSerializableFactory.unbind(jndiName);
Version two of the JNDIMapMBean interface and JNDIMap implementation class, which is based on the ServiceMBean interface and ServiceMBeanSupport class, is given in See JNDIMap MBean interface and implementation based on the ServiceMBean interface and ServiceMBeanSupport class.. In this version, the implementation class extends the ServiceMBeanSupport class and overrides the startService method and the stopService method. JNDIMapMBean also implements the abstract getName to return a descriptive name for the MBean. The JNDIMapMBean interface extends the org.jboss.system.ServiceMBean interface and only declares the setter and getter methods for the JndiName attribute because it inherits the Service life cycle methods from ServiceMBean . This is the third approach mentioned at the start of the "Writing JBoss MBean Services" section. The implementation differences between See JNDIMapMBean interface and implementation based on the service interface method pattern. and See JNDIMap MBean interface and implementation based on the ServiceMBean interface and ServiceMBeanSupport class. are highlighted in bold in See JNDIMap MBean interface and implementation based on the ServiceMBean interface and ServiceMBeanSupport class..
// The JNDIMap MBean interface
import javax.naming.NamingException;
public interface JNDIMapMBean extends org.jboss.system.ServiceMBean
public void setJndiName(String jndiName) throws NamingException;
// The JNDIMap MBean implementation
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.jboss.naming.NonSerializableFactory;
public class JNDIMap extends org.jboss.system.ServiceMBeanSupport
private HashMap contextMap = new HashMap();
public void setJndiName(String jndiName) throws NamingException
String oldName = this.jndiName;
if( super.getState() == STARTED )
NamingException ne = new NamingException("Failed to update jndiName");
public void startService() throws Exception
private void rebind() throws NamingException
InitialContext rootCtx = new InitialContext();
Name fullName = rootCtx.getNameParser("").parse(jndiName);
log.info("fullName="+fullName);
NonSerializableFactory.rebind(fullName, contextMap, true);
private void unbind(String jndiName)
InitialContext rootCtx = new InitialContext();
NonSerializableFactory.unbind(jndiName);
log.error("Failed to unbind map", e);
The source code for these MBeans along with the service descriptors is located in the examples/src/main/org/jboss/chap2/{ex1,ex2} directories.
The example 1 service descriptor is given in See The example 1 JNDIMap MBean service descriptor and a client usage code fragment.. along with a sample client usage code fragment. The JNDIMap MBean binds a HashMap object under the "inmemory/maps/MapTest" JNDI name and the client code fragment demonstrates retrieving the HashMap object from the "inmemory/maps/MapTest" location.
<!-- The SAR META-INF/jboss-service.xml descriptor -->
<mbean code="org.jboss.chap2.ex1.JNDIMap" name="chap2.ex1:service=JNDIMap">
<attribute name="JndiName">inmemory/maps/MapTest</attribute>
<depends>jboss:service=Naming</depends>
InitialContext ctx = new InitialContext();
HashMap map = (HashMap) ctx.lookup("inmemory/maps/MapTest");
In this section we will develop a variation of the JNDIMap MBean introduced in the preceding section that exposes its management metadata using the JBoss XMBean framework. Our core managed component will be exactly the same core code from the JNDIMap class, but this will not implement any specific management related interface. We will illustrate the following capabilities not possible with a Standard MBean:
Let's start with a simple XMBean variation of the standard MBean version of the JNDIMap that adds the descriptive information about the attributes and operations and their arguments. See The version 1 JNDIMap XMBean descriptor. shows the jboss-service.xml descriptor and the jndimap-xmbean1.xml XMBean descriptor. The source can be found in the src/main/org/jboss/chap2/xmbean directory of the book examples.
<?xml version='1.0' encoding='UTF-8' ?>
"-//JBoss//DTD MBean Service 3.2//EN"
"http://www.jboss.org/j2ee/dtd/jboss-service_3_2.dtd"
<mbean code="org.jboss.chap2.xmbean.JNDIMap" name="chap2.xmbean:service=JNDIMap"
xmbean-dd="META-INF/jndimap-xmbean.xml">
<attribute name="JndiName">inmemory/maps/MapTest</attribute>
<depends>jboss:service=Naming</depends>
<?xml version="1.0" encoding="UTF-8"?>
"-//JBoss//DTD JBOSS XMBEAN 1.0//EN"
"http://www.jboss.org/j2ee/dtd/jboss_xmbean_1_0.dtd">
<description>The JNDIMap XMBean Example Version 1</description>
<persistence persistPolicy="Never"
persistLocation="data/JNDIMap.data"
<currencyTimeLimit value="10"/>
<state-action-on-update value="keep-running"/>
<class>org.jboss.test.jmx.xmbean.JNDIMap</class>
<description>The default constructor</description>
<attribute access="read-write" getMethod="getJndiName" setMethod="setJndiName">
<description>The location in JNDI where the Map we manage will be bound</description>
<default value="inmemory/maps/MapTest" />
<attribute access="read-write" getMethod="getInitialValues" setMethod="setInitialValues">
<description>The array of initial values that will be placed into
the map associated with the service. The array is a collection of
key,value pairs with elements[0,2,4,...2n] being the keys and
elements [1,3,5,...,2n+1] the associated values. The "[Ljava.lang.String;"
type signature is the VM representation of the java.lang.String[] type.
<type>[Ljava.lang.String;</type>
<default value="key0,value0" />
<description>The start lifecycle operation</description>
<description>The stop lifecycle operation</description>
<description>Put a value into the map</description>
<description>The key the value will be store under</description>
<description>The value to place into the map</description>
<description>Get a value from the map</description>
<description>The key to lookup in the map</description>
<return-type>java.lang.Object</return-type>
<description>The notification sent whenever a value is get into the map
managed by the service</description>
<name>javax.management.Notification</name>
<notification-type>org.jboss.chap2.xmbean.JNDIMap.get</notification-type>
<description>The notification sent whenever a value is put into the map
managed by the service</description>
<name>javax.management.Notification</name>
<notification-type>org.jboss.chap2.xmbean.JNDIMap.put</notification-type>
As noted previously, 3.2.2 release replaced the binding of the RMIAdaptor interface with the invoker adaptor service and this service does not yet support remoting of JMX notifications. Therefore, we need to create a config that uses the RMIAdaptorService . There is a config target that sets up a rmi-adaptor configuration with the jmx-rmi-adaptor.sar installed. Build this setup using:
[nr@toki]$ ant -Dchap=chap2 config
[echo] Preparing rmi-adaptor configuration fileset
[copy] Copying 148 files to /tmp/jboss-3.2.3/server/rmi-adaptor
[copy] Copying 2 files to /tmp/jboss-3.2.3/server/rmi-adaptor/deploy/jmx-rmi-adaptor.sar
[delete] Deleting directory /tmp/jboss-3.2.3/server/rmi-adaptor/deploy/jmx-invoker-adaptor-server.sar
[delete] Deleting directory /tmp/jboss-3.2.3/server/rmi-adaptor/deploy/management
Now, run the rmi-adaptor configuration, and then build, deploy and test the XMBean as follows:
[nr@toki examples]$ ant -Dchap=chap2 -Dex=xmbean1 -Djboss.deploy.conf=rmi-adaptor run-example
[java] ImplementationTitle: JBoss [WonderLand]
[java] ImplementationVendor: JBoss.org
[java] ImplementationVersion: 3.2.2 (build: CVSTag=JBoss_3_2_2 date=2003101
[java] SpecificationTitle: JBoss
[java] SpecificationVendor: JBoss (http://www.jboss.org/)
[java] SpecificationVersion: 3.2.2
[java] JBoss version is: 3.2.2
[echo] Using jboss.dist=/cvs/Releases/jboss-3.2.2
[java] JNDIMap Class: org.jboss.mx.modelmbean.XMBean
[java] + void put(java.lang.Object chap2.xmbean:service=JNDIMap,java.lang.Object chap2.xmbean:service=JNDIMap)
[java] + java.lang.Object get(java.lang.Object chap2.xmbean:service=JNDIMap)
[java] + java.lang.String getJndiName()
[java] + void setJndiName(java.lang.String chap2.xmbean:service=JNDIMap)
[java] + [Ljava.lang.String; getInitialValues()
[java] + void setInitialValues([Ljava.lang.String; chap2.xmbean:service=JNDIMap)
[java] handleNotification, event: null
[java] handleNotification, event: javax.management.Notification[source=chap2.xmbean:service=JNDIMap,type=org.jboss.chap2.xmbean.JNDIMap.put,sequenceNumber=3,timeStamp=1083610215205,message=null,userData=null]
[java] JNDIMap.put(key1, value1) successful
[java] handleNotification, event: javax.management.Notification[source=chap2.xmbean:service=JNDIMap,type=org.jboss.chap2.xmbean.JNDIMap.get,sequenceNumber=4,timeStamp=1083610215376,message=null,userData=null]
[java] JNDIMap.get(key0): null
[java] handleNotification, event: javax.management.Notification[source=chap2.xmbean:service=JNDIMap,type=org.jboss.chap2.xmbean.JNDIMap.get,sequenceNumber=5,timeStamp=1083610215406,message=null,userData=null]
[java] JNDIMap.get(key1): value1
[java] handleNotification, event: javax.management.Notification[source=chap2.xmbean:service=JNDIMap,type=org.jboss.chap2.xmbean.JNDIMap.put,sequenceNumber=6,timeStamp=1083610215443,message=null,userData=null]
[java] handleNotification, event: javax.management.AttributeChangeNotification: source=chap2.xmbean:service=JNDIMap seq-no=3 time=1083610215448 message=InitialValues changed from javax.management.Attribute: name=InitialValues value=[Ljava.lang.String;@e02ee9 to javax.management.Attribute: name=InitialValues value=[Ljava.lang.String;@229bc1 attributeName=InitialValues attributeType=[Ljava.lang.String; oldValue=[Ljava.lang.String;@2445d7 newValue=[Ljava.lang.String;@65547d notificationType=jmx.attribute.change userData=null
The functionality is largely the same as the Standard MBean with the notable exception of the JMX notifications. A Standard MBean has no way of declaring that it will emit notifications. An XMBean may declare the notifications it emits using notification elements as is shown in the version 1 descriptor. We see the notifications from the get and put operations on the test client console output. Note that there is also an jmx.attribute.change notification emitted when the InitialValues attribute was changed. This is a standard feature of ModelMBean s owning to the fact that the ModelMBean interface extends the ModelMBeanNotificationBroadcaster which supports AttributeChangeNotificationListeners .
The other major difference between the Standard and XMBean versions of JNDIMap is the descriptive metadata. Look at the "chap2.xmbean:service=JNDIMap" in the jmx-console , and you will see the attributes section as shown in See The Version 1 JNDIMapXMBean jmx-console view..
Notice that the jmx-console now displays the full attribute description as specified in the xmbean descriptor rather than "MBean Attribute" seen in standard MBean implementations. Scroll down to the operations and you will also see that these now also have nice descriptions of their function and parameters.
In version 2 of the XMBean we add support for persistence of the XMBean attributes. The updated XMBean deployment descriptor is given in Lxxx2. The changes with respect to the version 1 descriptor of Lxxx1 are shown in bold.
<?xml version="1.0" encoding="UTF-8"?>
"-//JBoss//DTD JBOSS XMBEAN 1.0//EN"
"http://www.jboss.org/j2ee/dtd/jboss_xmbean_1_0.dtd">
<description>The JNDIMap XMBean Example Version 2</description>
<persistence persistPolicy="OnUpdate"
persistLocation="${jboss.server.data.dir}"
<currencyTimeLimit value="10"/>
<state-action-on-update value="keep-running"/>
<persistence-manager value="org.jboss.mx.persistence.ObjectStreamPersistenceManager" />
<class>org.jboss.test.jmx.xmbean.JNDIMap</class>
<description>The default constructor</description>
<attribute access="read-write" getMethod="getJndiName" setMethod="setJndiName">
<description>The location in JNDI where the Map we manage will be bound</description>
<default value="inmemory/maps/MapTest" />
<attribute access="read-write" getMethod="getInitialValues" setMethod="setInitialValues">
<description>The array of initial values that will be placed into
the map associated with the service. The array is a collection of
key,value pairs with elements[0,2,4,...2n] being the keys and
elements [1,3,5,...,2n+1] the associated values</description>
<type>[Ljava.lang.String;</type>
<default value="key0,value0" />
<description>The start lifecycle operation</description>
<description>The stop lifecycle operation</description>
<description>Put a value into the nap</description>
<description>The key the value will be store under</description>
<description>The value to place into the map</description>
<description>Get a value from the map</description>
<description>The key to lookup in the map</description>
<return-type>java.lang.Object</return-type>
<description>The notification sent whenever a value is get into the map
managed by the service</description>
<name>javax.management.Notification</name>
<notification-type>org.jboss.chap2.xmbean.JNDIMap.get</notification-type>
<description>The notification sent whenever a value is put into the map
managed by the service</description>
<name>javax.management.Notification</name>
<notification-type>org.jboss.chap2.xmbean.JNDIMap.put</notification-type>
Build, deploy and test the version 2 XMBean as follows:
[nr@toki examples]$ ant -Dchap=chap2 -Dex=xmbean2 -Djboss.deploy.conf=rmi-adaptor run-example
[java] ImplementationTitle: JBoss [WonderLand]
[java] ImplementationVendor: JBoss.org
[java] ImplementationVersion: 3.2.2 (build: CVSTag=JBoss_3_2_2 date=200310182216)
[java] SpecificationTitle: JBoss
[java] SpecificationVendor: JBoss (http://www.jboss.org/)
[java] SpecificationVersion: 3.2.2
[java] JBoss version is: 3.2.2
[echo] Using jboss.dist=/cvs/Releases/jboss-3.2.2
[delete] Deleting: C:\cvs\Releases\jboss-3.2.2\server\rmi-adaptor\deploy\chap2-ex1xmbean1.sar
[copy] Copying 1 file to C:\cvs\Releases\jboss-3.2.2\server\rmi-adaptor\deploy
[java] JNDIMap Class: org.jboss.mx.modelmbean.XMBean
[java] + void put(java.lang.Object chap2.xmbean:service=JNDIMap,java.lang.Object chap2.xmbean:service=JNDIMap)
[java] + java.lang.Object get(java.lang.Object chap2.xmbean:service=JNDIMap)
[java] + java.lang.String getJndiName()
[java] + void setJndiName(java.lang.String chap2.xmbean:service=JNDIMap)
[java] + [Ljava.lang.String; getInitialValues()
[java] + void setInitialValues([Ljava.lang.String; chap2.xmbean:service=JNDIMap)
[java] handleNotification, event: null
[java] handleNotification, event: javax.management.Notification[source=chap2.xmbean:service=JNDIMap,type=org.jboss.chap2.xmbean.JNDIMap.put,sequenceNumber=3,timeStamp=1068684154664,message=null,userData=null]
[java] JNDIMap.put(key1, value1) successful
[java] handleNotification, event: javax.management.Notification[source=chap2.xmbean:service=JNDIMap,type=org.jboss.chap2.xmbean.JNDIMap.get,sequenceNumber=4,timeStamp=1068684154664,message=null,userData=null]
[java] JNDIMap.get(key0): null
[java] handleNotification, event: javax.management.Notification[source=chap2.xmbean:service=JNDIMap,type=org.jboss.chap2.xmbean.JNDIMap.get,sequenceNumber=5,timeStamp=1068684154674,message=null,userData=null]
[java] JNDIMap.get(key1): value1
[java] handleNotification, event: javax.management.Notification[source=chap2.xmbean:service=JNDIMap,type=org.jboss.chap2.xmbean.JNDIMap.put,sequenceNumber=6,timeStamp=1068684154724,message=null,userData=null]
[java] handleNotification, event: javax.management.AttributeChangeNotification: source=chap2.xmbean:service=JNDIMap seq-no=3 time=1068684154744 message=InitialValues changed from javax.management.Attribute: name=InitialValues value=[Ljava.lang.String;@867fad to javax.management.Attribute: name=InitialValues value=[Ljava.lang.String;@e62121 attributeName=InitialValues attributeType=[Ljava.lang.String; oldValue=[Ljava.lang.String;@1833eca newValue=[Ljava.lang.String;@18f5824 notificationType=jmx.attribute.change userData=null
There is nothing manifestly different about this version of the XMBean at this point because we have done nothing to test that changes to attribute value are actually persisted. Perform this test by running example xmbean2a serveral times:
[nr@toki examples] ant -Dchap=chap2 -Dex=xmbean2a -Djboss.deploy.conf=rmi-adaptor run-example
[copy] Copying 1 file to /tmp/jboss-3.2.3/server/rmi-adaptor/deploy
[java] key=key10, value=value10
[nr@toki examples] ant -Dchap=2 -Dex=xmbean2a -Djboss.deploy.conf=rmi-adaptor run-example
[copy] Copying 1 file to /tmp/jboss-3.2.3/server/rmi-adaptor/deploy
[java] key=key10, value=value10
[nr@toki examples] ant -Dchap=chap2 -Dex=xmbean2a -Djboss.deploy.conf=rmi-adaptor run-example
[copy] Copying 1 file to /tmp/jboss-3.2.3/server/rmi-adaptor/deploy
[java] key=key10, value=value10
The org.jboss.chap2.xmbean.TestXMBeanRestart used in this example obtains the current InitialValues attribute setting, and then adds another key/value pair to it. The client code is shown in See The TestXMBeanRestart persistence test client..
package org.jboss.chap2.xmbean;
import javax.management.Attribute;
import javax.management.ObjectName;
import javax.naming.InitialContext;
import org.jboss.jmx.adaptor.rmi.RMIAdaptor;
/** A client that demonstrates the persistence of the xmbean attributes. Every
time it it run it looks up the InitialValues attribute, prints it out
and then adds a new key/value to the list.
@author [email protected]
public class TestXMBeanRestart
* @param args the command line arguments
public static void main(String[] args) throws Exception
InitialContext ic = new InitialContext();
RMIAdaptor server = (RMIAdaptor) ic.lookup("jmx/rmi/RMIAdaptor");
// Get the InitialValues attribute
ObjectName name = new ObjectName("chap2.xmbean:service=JNDIMap");
String[] initialValues = (String[]) server.getAttribute(name, "InitialValues");
System.out.println("InitialValues.length="+initialValues.length);
int length = initialValues.length;
for(int n = 0; n < length; n += 2)
String key = initialValues[n];
String value = initialValues[n+1];
System.out.println("key="+key+", value="+value);
String[] newInitialValues = new String[length+2];
System.arraycopy(initialValues, 0, newInitialValues, 0, length);
newInitialValues[length] = "key"+(length/2+1);
newInitialValues[length+1] = "value"+(length/2+1);
Attribute ivalues = new Attribute("InitialValues", newInitialValues);
server.setAttribute(name, ivalues);
At this point you may even shutdown the JBoss server, restart it and then rerun the initial example 2 to see if the changes are persisted across server restarts:
[nr@toki examples]$ ant -Dchap=chap2 -Dex=xmbean2 -Djboss.deploy.conf=rmi-adaptor run-example
[java] JNDIMap Class: org.jboss.mx.modelmbean.XMBean
[java] + void put(java.lang.Object chap2.xmbean:service=JNDIMap,java.lang.Object chap2.xmbean:service=JNDIMap)
[java] + java.lang.Object get(java.lang.Object chap2.xmbean:service=JNDIMap)
[java] + java.lang.String getJndiName()
[java] + void setJndiName(java.lang.String chap2.xmbean:service=JNDIMap)
[java] + [Ljava.lang.String; getInitialValues()
[java] + void setInitialValues([Ljava.lang.String; chap2.xmbean:service=JNDIMap)
[java] handleNotification, event: null
[java] key=key10, value=value10
[java] handleNotification, event: javax.management.Notification[source=chap2.xmbean:service=JNDIMap,type=org.jboss.chap2.xmbean.JNDIMap.put,sequenceNumber=3,timeStamp=1083612274379,message=null,userData=null]
[java] JNDIMap.put(key1, value1) successful
[java] handleNotification, event: javax.management.Notification[source=chap2.xmbean:service=JNDIMap,type=org.jboss.chap2.xmbean.JNDIMap.get,sequenceNumber=4,timeStamp=1083612274546,message=null,userData=null]
[java] JNDIMap.get(key0): null
[java] handleNotification, event: javax.management.Notification[source=chap2.xmbean:service=JNDIMap,type=org.jboss.chap2.xmbean.JNDIMap.get,sequenceNumber=5,timeStamp=1083612274581,message=null,userData=null]
[java] JNDIMap.get(key1): value1
[java] handleNotification, event: javax.management.Notification[source=chap2.xmbean:service=JNDIMap,type=org.jboss.chap2.xmbean.JNDIMap.put,sequenceNumber=6,timeStamp=1083612274669,message=null,userData=null]
[java] handleNotification, event: javax.management.Notification[source=chap2.xmbean:service=JNDIMap,type=org.jboss.chap2.xmbean.JNDIMap.put,sequenceNumber=7,timeStamp=1083612274696,message=null,userData=null]
[java] handleNotification, event: javax.management.Notification[source=chap2.xmbean:service=JNDIMap,type=org.jboss.chap2.xmbean.JNDIMap.put,sequenceNumber=8,timeStamp=1083612274721,message=null,userData=null]
[java] handleNotification, event: javax.management.Notification[source=chap2.xmbean:service=JNDIMap,type=org.jboss.chap2.xmbean.JNDIMap.put,sequenceNumber=9,timeStamp=1083612274757,message=null,userData=null]
[java] handleNotification, event: javax.management.AttributeChangeNotification: source=chap2.xmbean:service=JNDIMap seq-no=5 time=1083612274788 message=InitialValues changed from javax.management.Attribute: name=InitialValues value=[Ljava.lang.String;@a0ba6c to javax.management.Attribute: name=InitialValues value=[Ljava.lang.String;@3c0543 attributeName=InitialValues attributeType=[Ljava.lang.String; oldValue=[Ljava.lang.String;@c8ccb1 newValue=[Ljava.lang.String;@f6852d notificationType=jmx.attribute.change userData=null
You see that the last InitialValues attribute setting is in fact visible.
The last example version of the JNDIMap XMBean will demonstrate customization of the server interceptor stack as well as exposing a subset of the XMBean management interface via a typed proxy to a remote client using RMI/JRMP. On the server side we will add a simple security interceptor that only allows access to attributes or operations by a user specified in the interceptor configuration. We will also use another custom interceptor to implement the MBean detached invoker pattern described in a latter section entitled "See Remote Access to Services, Detached Invokers.". By implementing this pattern in an invoker rather than the XMBean, we demonstrate how to introduce a remote access aspect without having to modify the existing JNDMap implementation.
We will use the JRMPProxyFactory service to expose the ClientInterface shown in See The ClientInterface view of the JNDIMap XMBean exposed via RMI/JRMP. to remote clients. Our test client will obtain the ClientInterface proxy from JNDI and interact with the XMBean through RMI style calls instead of the RMIAdaptor and MBeanServer style used previously.
public interface ClientInterface
public String[] getInitialValues();
public void setInitialValues(String[] keyValuePairs);
public Object get(Object key);
package org.jboss.chap2.xmbean;
import javax.naming.InitialContext;
import org.jboss.security.SecurityAssociation;
import org.jboss.security.SimplePrincipal;
/** A client that accesses an XMBean through its RMI interface
@author [email protected]
* @param args the command line arguments
public static void main(String[] args) throws Exception
InitialContext ic = new InitialContext();
ClientInterface xmbean = (ClientInterface) ic.lookup("secure-xmbean/ClientInterface");
// This call should fail because we have not set a security context
String[] tmp = xmbean.getInitialValues();
throw new IllegalStateException("Was able to call getInitialValues");
System.out.println("Called to getInitialValues failed as expected: "
// Set a security context using the SecurityAssociation
SecurityAssociation.setPrincipal(new SimplePrincipal("admin"));
// Get the InitialValues attribute
String[] initialValues = xmbean.getInitialValues();
for(int n = 0; n < initialValues.length; n += 2)
String key = initialValues[n];
String value = initialValues[n+1];
System.out.println("key="+key+", value="+value);
// Invoke the put(Object, Object) op
System.out.println("JNDIMap.put(key1, value1) successful");
Object result0 = xmbean.get("key0");
System.out.println("JNDIMap.get(key0): "+result0);
Object result1 = xmbean.get("key1");
System.out.println("JNDIMap.get(key1): "+result1);
xmbean.setInitialValues(initialValues);
initialValues = xmbean.getInitialValues();
for(int n = 0; n < initialValues.length; n += 2)
String key = initialValues[n];
String value = initialValues[n+1];
<?xml version="1.0" encoding="UTF-8"?>
"-//JBoss//DTD JBOSS XMBEAN 1.0//EN"
"http://www.jboss.org/j2ee/dtd/jboss_xmbean_1_0.dtd"
<!ATTLIST interceptor adminName CDATA #IMPLIED>
<description>The JNDIMap XMBean Example Version 3</description>
<interceptor code="org.jboss.chap2.xmbean.ServerSecurityInterceptor"
<interceptor code="org.jboss.chap2.xmbean.InvokerInterceptor" />
<interceptor code="org.jboss.mx.interceptor.PersistenceInterceptor2" />
<interceptor code="org.jboss.mx.interceptor.ModelMBeanInterceptor" />
<interceptor code="org.jboss.mx.interceptor.ObjectReferenceInterceptor" />
<persistence persistPolicy="Never" />
<currencyTimeLimit value="10"/>
<state-action-on-update value="keep-running"/>
<class>org.jboss.test.jmx.xmbean.JNDIMap</class>
<description>The default constructor</description>
<attribute access="read-write" getMethod="getJndiName" setMethod="setJndiName">
<description>The location in JNDI where the Map we manage will be bound</description>
<default value="inmemory/maps/MapTest" />
<attribute access="read-write" getMethod="getInitialValues" setMethod="setInitialValues">
<description>The array of initial values that will be placed into
the map associated with the service. The array is a collection of
key,value pairs with elements[0,2,4,...2n] being the keys and
elements [1,3,5,...,2n+1] the associated values</description>
<type>[Ljava.lang.String;</type>
<default value="key0,value0" />
<description>The start lifecycle operation</description>
<description>The stop lifecycle operation</description>
<description>Put a value into the nap</description>
<description>The key the value will be store under</description>
<description>The value to place into the map</description>
<description>Get a value from the map</description>
<description>The key to lookup in the map</description>
<return-type>java.lang.Object</return-type>
The addition over the previous versions of the JNDIMap XMBean is the interceptors element shown in bold in the listing. This defines the interceptor stack through which all MBean attribute access and operations pass. The first two interceptors, org.jboss.chap2.xmbean.ServerSecurityInterceptor and org.jboss.chap2.xmbean.InvokerInterceptor are the example custom interceptors. The remaining three interceptors are the standard ModelMBean interceptors. Because we have a persistence policy of Never, we could in fact remove the standard org.jboss.mx.interceptor.PersistenceInterceptor2 . The JMX interceptors are an ordered chain of filters. The standard base class of an interceptor is shown in See The JMX AbstractInterceptor base class..
package org.jboss.mx.interceptor;
import javax.management.MBeanInfo;
import org.jboss.mx.server.MBeanInvoker;
* Base class for all interceptors.
* @see org.jboss.mx.interceptor.StandardMBeanInterceptor
* @see org.jboss.mx.interceptor.LogInterceptor
* @author <a href="mailto:[email protected]">Juha Lindfors</a>.
* @version $Revision: 1.4.2.2 $
public class AbstractInterceptor implements Interceptor
// Attributes ----------------------------------------------------
protected Interceptor next = null;
protected MBeanInvoker invoker;
// Constructors --------------------------------------------------
public AbstractInterceptor(String name)
public AbstractInterceptor(MBeanInfo info, MBeanInvoker invoker)
this.name = getClass().getName();
// Public --------------------------------------------------------
public Object invoke(Invocation invocation) throws InvocationException
return getNext().invoke(invocation);
public Interceptor setNext(Interceptor interceptor)
The custom interceptors for the version 3 XMBean example are shown in See The SecurityInterceptor. and See The InvokerInterceptor.. See The SecurityInterceptor. shows the SecurityInterceptor while See The InvokerInterceptor. shows the InvokerInterceptor .
package org.jboss.chap2.xmbean;
import java.security.Principal;
import org.jboss.logging.Logger;
import org.jboss.mx.interceptor.AbstractInterceptor;
import org.jboss.mx.interceptor.Invocation;
import org.jboss.mx.interceptor.InvocationException;
import org.jboss.security.SimplePrincipal;
/** A simple security interceptor example that restricts access to a single
* @author [email protected]
public class ServerSecurityInterceptor extends AbstractInterceptor
private static Logger log = Logger.getLogger(ServerSecurityInterceptor.class);
private SimplePrincipal admin = new SimplePrincipal("admin");
public void setAdminName(String name)
admin = new SimplePrincipal(name);
public Object invoke(Invocation invocation) throws InvocationException
String opName = invocation.getName();
// If this is not the invoke(Invocation) op just pass it along
if( opName.equals("invoke") == false )
return getNext().invoke(invocation);
Object[] args = invocation.getArgs();
org.jboss.invocation.Invocation invokeInfo =
(org.jboss.invocation.Invocation) args[0];
Principal caller = invokeInfo.getPrincipal();
log.info("invoke, opName="+opName+", caller="+caller);
// Only the admin caller is allowed access
if( caller == null || caller.equals(admin) == false )
throw new InvocationException(
new SecurityException("Caller="+caller+" is not allowed access")
package org.jboss.chap2.xmbean;
import java.lang.reflect.Method;
import javax.management.Descriptor;
import javax.management.MBeanInfo;
import org.jboss.logging.Logger;
import org.jboss.mx.interceptor.AbstractInterceptor;
import org.jboss.mx.interceptor.Invocation;
import org.jboss.mx.interceptor.InvocationException;
import org.jboss.mx.server.MBeanInvoker;
import org.jboss.invocation.MarshalledInvocation;
/** An interceptor that handles the
* @author [email protected]
public class InvokerInterceptor extends AbstractInterceptor
private static Logger log = Logger.getLogger(InvokerInterceptor.class);
private Class exposedInterface = ClientInterface.class;
private HashMap methodMap = new HashMap();
private HashMap invokeMap = new HashMap();
public InvokerInterceptor(MBeanInfo info, MBeanInvoker invoker)
Descriptor[] descriptors = invoker.getDescriptors();
Object resource = invoker.getResource();
Class[] getInitialValuesSig = {};
Method getInitialValues = exposedInterface.getDeclaredMethod("getInitialValues",
Long hash = new Long(MarshalledInvocation.calculateHash(getInitialValues));
InvocationInfo invokeInfo = new InvocationInfo("InitialValues",
Invocation.ATTRIBUTE, Invocation.READ, getInitialValuesSig,
methodMap.put(hash, getInitialValues);
invokeMap.put(getInitialValues, invokeInfo);
log.debug("getInitialValues hash:"+hash);
Class[] setInitialValuesSig = {String[].class};
Method setInitialValues = exposedInterface.getDeclaredMethod("setInitialValues",
hash = new Long(MarshalledInvocation.calculateHash(setInitialValues));
invokeInfo = new InvocationInfo("InitialValues",
Invocation.ATTRIBUTE, Invocation.WRITE, setInitialValuesSig,
methodMap.put(hash, setInitialValues);
invokeMap.put(setInitialValues, invokeInfo);
log.debug("setInitialValues hash:"+hash);
Class[] getSig = {Object.class};
Method get = exposedInterface.getDeclaredMethod("get",
hash = new Long(MarshalledInvocation.calculateHash(get));
invokeInfo = new InvocationInfo("get",
Invocation.OPERATION, Invocation.READ, getSig,
invokeMap.put(get, invokeInfo);
Class[] putSig = {Object.class, Object.class};
Method put = exposedInterface.getDeclaredMethod("put",
hash = new Long(MarshalledInvocation.calculateHash(put));
invokeInfo = new InvocationInfo("put",
Invocation.OPERATION, Invocation.WRITE, putSig,
invokeMap.put(put, invokeInfo);
log.error("Failed to init InvokerInterceptor", e);
public Object invoke(Invocation invocation) throws InvocationException
String opName = invocation.getName();
Object[] args = invocation.getArgs();
if( opName.equals("invoke") == true )
org.jboss.invocation.Invocation invokeInfo =
(org.jboss.invocation.Invocation) args[0];
// Set the method hash to Method mapping
if (invokeInfo instanceof MarshalledInvocation)
MarshalledInvocation mi = (MarshalledInvocation) invokeInfo;
// Invoke the exposedInterface method via reflection if this is an invoke
Method method = invokeInfo.getMethod();
Object[] methodArgs = invokeInfo.getArguments();
InvocationInfo info = (InvocationInfo) invokeMap.get(method);
Invocation methodInvocation = info.getInvocation(methodArgs);
returnValue = getNext().invoke(methodInvocation);
returnValue = getNext().invoke(invocation);
/** A class that holds the ClientInterface method info needed to build
* the JMX Invocation to pass down the interceptor stack.
private Descriptor[] descriptors;
InvocationInfo(String name, int type, int impact,
Class[] signature, Descriptor[] descriptors, Object resource)
this.descriptors = descriptors;
this.signature = new String[signature.length];
for(int s = 0; s < signature.length; s ++)
this.signature[s] = signature[s].getName();
Invocation getInvocation(Object[] args)
return new Invocation(name, type, impact, args, signature,
The ServerSecurityInterceptor intercepts invoke operations and validates that the Invocation context include an admin principal.
The InvokerInterceptor implements the detached invoker pattern. This is discussed in detail in Remote Access to Services, Detached Invokers.
<?xml version='1.0' encoding='UTF-8' ?>
PUBLIC "-//JBoss//DTD MBean Service 3.2//EN"
"http://www.jboss.org/j2ee/dtd/jboss-service_3_2.dtd"
This instance goes beyond the jboss-service_3_2.dtd model
due to its use of the embedded <interceptors> element in the
ClientInterceptors attribute of the JRMPProxyFactory config.
<mbean code="org.jboss.chap2.xmbean.JNDIMap"
name="chap2.xmbean:service=JNDIMap,version=3"
xmbean-dd="META-INF/jndimap-xmbean3.xml">
<attribute name="JndiName">inmemory/maps/MapTest</attribute>
<depends>jboss:service=Naming</depends>
<!-- The JRMP invoker proxy configuration for the naming service -->
<mbean code="org.jboss.invocation.jrmp.server.JRMPProxyFactory"
name="jboss.test:service=proxyFactory,type=jrmp,target=JNDIMap">
<!-- Use the standard JRMPInvoker from conf/jboss-service.xxml -->
<attribute name="InvokerName">jboss:service=invoker,type=jrmp</attribute>
<attribute name="TargetName">chap2.xmbean:service=JNDIMap,version=3</attribute>
<attribute name="JndiName">secure-xmbean/ClientInterface</attribute>
<attribute name="ExportedInterface">org.jboss.chap2.xmbean.ClientInterface</attribute>
<attribute name="ClientInterceptors">
<interceptor>org.jboss.proxy.ClientMethodInterceptor</interceptor>
<interceptor>org.jboss.proxy.SecurityInterceptor</interceptor>
<interceptor>org.jboss.invocation.InvokerInterceptor</interceptor>
<depends>jboss:service=invoker,type=jrmp</depends>
<depends>chap2.xmbean:service=JNDIMap,version=3</depends>
[nr@toki examples] ant -Dchap=chap2 -Dex=xmbean3 config
[echo] Preparing rmi-adaptor configuration fileset
[copy] Copying 5 files to /tmp/jboss-3.2.3/server/rmi-adaptor
[delete] Deleting directory /tmp/jboss-3.2.3/server/rmi-adaptor/deploy/jmx-invoker-adaptor-server.sar
[delete] Deleting directory /tmp/jboss-3.2.3/server/rmi-adaptor/deploy/management
[nr@toki examples]$ ant -Dchap=chap2 -Dex=xmbean3 run-example
[java] Called to getInitialValues failed as expected: Caller=null is not allowed access
[java] JNDIMap.put(key1, value1) successful
[java] JNDIMap.get(key0): null
[java] JNDIMap.get(key1): value1
[java] key=key0.1, value=value0.2
[starksm@banshee examples]$ ant -Dchap=chap2 -Dex=xmbean3 run-example
[java] Called to getInitialValues failed as expected: Caller=null is not allowed access
[java] key=key0.1, value=value0.2
[java] JNDIMap.put(key1, value1) successful
[java] JNDIMap.get(key0): null
[java] JNDIMap.get(key1): value1
We have seen how to manage dependencies using the service descriptor depends and depends-list tags. The deployment ordering supported by the deployment scanners provides a coarse-grained dependency management in that there is an order to deployments. If dependencies are consistent with the deployment packages then this is a simpler mechanism than having to enumerate the explicit MBean-MBean dependencies. By writing your own filters you can change the coarse grained ordering performed by the deployment scanner.
When a component archive is deployed, its nested deployment units are processed in a depth first ordering. Structuring of components into an archive hierarchy is yet another way to mange deployment ordering.
Typically you will need to explicitly state your MBean dependencies because your packaging structure does not happen to resolve the dependencies. Let's consider an example component deployment that consists of an MBean that uses an EJB. See An example ear with an MBean that depends on an EJB. shows the example EAR structure.
+- chap2-ex3.jar (archive) [EJB jar]
| +- org/jboss/chap2/ex3/EchoBean.class
| +- org/jboss/chap2/ex3/EchoLocal.class
| +- org/jboss/chap2/ex3/EchoLocalHome.class
+- chap2-ex3.sar (archive) [MBean sar]
| +- META-INF/jboss-service.xml
| +- org/jboss/chap2/ex3/EjbMBeanAdaptor.class
The EAR contains a chap2-ex3.jar and chap2-ex3.sar. The chap2-ex3.jar is the EJB archive and the chap2-ex3.sar is the MBean service archive. We have implemented the service as a DynamicMBean to provide an illustration of their use. See A DynamicMBean service that uses and EJB. shows the code for the EjbMBeanAdaptor MBean service.
Believe it or not, this is a very trivial MBean. The vast majority of the code is there to provide the MBean metadata and handle the callbacks from the MBeanServer . This is required because a DynamicMBean is free to expose whatever management interface it wants. A DynamicMBean can in fact change its management interface at runtime simply by returning a different metadata value from the getMBeanInfo method. Of course, clients may not be too happy with such a dynamic object, but the MBeanServer will do nothing to prevent a DynamicMBean from changing its interface.
There are two points to this example. First, demonstrate how an MBean can depend on an EJB for some of its functionality and second, how to create MBeans with dynamic management interfaces. If we were to write a standard MBean with a static interface for this example it would be as given in See The standard MBean interface for Listing 2-31..
public interface EjbMBeanAdaptorMBean
public String getHelloPrefix();
public void setHelloPrefix(String prefix);
public String getEjbJndiName();
public void setEjbJndiName(String jndiName);
public String echo(String arg) throws CreateException, NamingException;
public void create() throws Exception;
public void start() throws Exception;
Moving to lines 67-83, this is where the MBean operation metadata is constructed. The echo(String) , create() , start() , stop() and destroy() operations are defined by obtaining the corresponding java.lang.reflect.Method object and adding a description.Let's go through the code and discuss where this interface implementation exists and how the MBean uses the EJB. Beginning with lines 40-51, the two MBeanAttributeInfo instances created define the attributes of the MBean. These attributes correspond to the getHelloPrefix / setHelloPrefix and getEjbJndiName / setEjbJndiName of the static interface. One thing to note in terms of why one might want to use a DynamicMBean is that you have the ability to associate descriptive text with the attribute metadata. This is not something you can do with a static interface.
Lines 88-103 correspond to the JBoss service life cycle callbacks. Since we are subclassing the ServiceMBeanSupport utility class, we override the createService , startService , and stopService template callbacks rather than the create , start , and stop methods of the service interface. Note that we cannot attempt to lookup the EchoLocalHome interface of the EJB we make use of until the startService method. Any attempt to access the home interface in an earlier life cycle method would result in the name not being found in JNDI because the EJB container had not gotten to the point of binding the home interfaces. Because of this dependency we will need to specify that the MBean service depends on the EchoLocal EJB container to ensure that the service is not started before the EJB container is started. We will see this dependency specification when we look at the service descriptor.
Lines 105-121 are the HelloPrefix and EjbJndiName attribute accessors implementations. These are invoked in response to getAttribute/setAttribute invocations made through the MBeanServer .
Lines 123-130 correspond to the echo(String) operation implementation. This method invokes the EchoLocal.echo(String) EJB method. The local bean interface is created using the EchoLocalHome that was obtained in the startService method.
The remainder of the class makes up the DynamicMBean interface implementation. Lines 133-152 correspond to the MBean metadata accessor callback. This method returns a description of the MBean management interface in the form of the javax.management.MBeanInfo object. This is made up of a description, the MBeanAttributeInfo and MBeanOperationInfo metadata created earlier, as well as constructor and notification information. This MBean does not need any special constructors or notifications so this information is null.
Lines 154-258 handle the attribute access requests. This is rather tedious and error prone code so a toolkit or infrastructure that helps generate these methods should be used. A ModelMBean framework based on XML called XBeans is currently being investigated in JBoss. Other than this, no other DynamicMBean frameworks currently exist.
Lines 260-310 correspond to the operation invocation dispatch entry point. Here the request operation action name is checked against those the MBean handles and the appropriate method is invoked.
The jboss-service.xml descriptor for the MBean is given in See The DynamicMBean jboss-service.xml descriptor.. The dependency on the EJB container MBean is highlighted in bold. The format of the EJB container MBean ObjectName is:
"jboss.j2ee:service=EJB,jndiName=" + <home-jndi-name>
where the <home-jndi-name> is the EJB home interface JNDI name.
<mbean code="org.jboss.chap2.ex3.EjbMBeanAdaptor"
name="jboss.book:service=EjbMBeanAdaptor">
<attribute name="HelloPrefix">AdaptorPrefix</attribute>
<attribute name="EjbJndiName">local/chap2.EchoBean</attribute>
<depends>jboss.j2ee:service=EJB,jndiName=local/chap2.EchoBean</depends>
Deploy the example ear by running:
[starksm@banshee examples]$ ant -Dchap=chap2 -Dex=3 run-example
[copy] Copying 1 file to /tmp/jboss-3.2.3/server/default/deploy
On the server console there will be messages similar to the following:
15:00:38,457 INFO [MainDeployer] Starting deployment of package: file:/private/tmp/jboss-3.2.3/server/default/deploy/chap2-ex3.ear
15:00:38,564 INFO [EARDeployer] Init J2EE application: file:/private/tmp/jboss-3.2.3/server/default/deploy/chap2-ex3.ear
15:00:38,875 INFO [EjbMBeanAdaptor] preRegister notification seen
15:00:38,928 INFO [EjbMBeanAdaptor] Don't panic, just a stack trace
java.lang.Throwable: getMBeanInfo trace
at org.jboss.chap2.ex3.EjbMBeanAdaptor.getMBeanInfo(EjbMBeanAdaptor.java:153)
at org.jboss.mx.server.MBeanServerImpl.getMBeanInfo(MBeanServerImpl.java:568)
at org.jboss.system.ServiceConfigurator.configure(ServiceConfigurator.java:215)
at org.jboss.system.ServiceConfigurator.internalInstall(ServiceConfigurator.java:172)
at org.jboss.system.ServiceConfigurator.install(ServiceConfigurator.java:114)
at org.jboss.system.ServiceController.install(ServiceController.java:225)
at sun.reflect.GeneratedMethodAccessor29.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at org.jboss.mx.capability.ReflectedMBeanDispatcher.invoke(ReflectedMBeanDispatcher.java:284)
at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:546)
at org.jboss.mx.util.MBeanProxyExt.invoke(MBeanProxyExt.java:177)
at $Proxy4.install(Unknown Source)
at org.jboss.deployment.SARDeployer.create(SARDeployer.java:183)
at org.jboss.deployment.MainDeployer.create(MainDeployer.java:786)
at org.jboss.deployment.MainDeployer.create(MainDeployer.java:778)
at org.jboss.deployment.MainDeployer.deploy(MainDeployer.java:641)
at org.jboss.deployment.MainDeployer.deploy(MainDeployer.java:605)
at sun.reflect.GeneratedMethodAccessor20.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at org.jboss.mx.capability.ReflectedMBeanDispatcher.invoke(ReflectedMBeanDispatcher.java:284)
at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:546)
at org.jboss.mx.util.MBeanProxyExt.invoke(MBeanProxyExt.java:177)
at $Proxy6.deploy(Unknown Source)
at org.jboss.deployment.scanner.URLDeploymentScanner.deploy(URLDeploymentScanner.java:302)
at org.jboss.deployment.scanner.URLDeploymentScanner.scan(URLDeploymentScanner.java:476)
at org.jboss.deployment.scanner.AbstractDeploymentScanner$ScannerThread.doScan(AbstractDeploymentScanner.java:201)
at org.jboss.deployment.scanner.AbstractDeploymentScanner$ScannerThread.loop(AbstractDeploymentScanner.java:212)
at org.jboss.deployment.scanner.AbstractDeploymentScanner$ScannerThread.run(AbstractDeploymentScanner.java:191)
15:00:39,028 INFO [EjbMBeanAdaptor] Don't panic, just a stack trace
java.lang.Throwable: getMBeanInfo trace
at org.jboss.chap2.ex3.EjbMBeanAdaptor.getMBeanInfo(EjbMBeanAdaptor.java:153)
at org.jboss.mx.server.MBeanServerImpl.getMBeanInfo(MBeanServerImpl.java:568)
at org.jboss.system.ServiceController.getServiceProxy(ServiceController.java:745)
at org.jboss.system.ServiceController.create(ServiceController.java:276)
at org.jboss.system.ServiceController.create(ServiceController.java:243)
at sun.reflect.GeneratedMethodAccessor4.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at org.jboss.mx.capability.ReflectedMBeanDispatcher.invoke(ReflectedMBeanDispatcher.java:284)
at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:546)
at org.jboss.mx.util.MBeanProxyExt.invoke(MBeanProxyExt.java:177)
at $Proxy4.create(Unknown Source)
at org.jboss.deployment.SARDeployer.create(SARDeployer.java:192)
at org.jboss.deployment.MainDeployer.create(MainDeployer.java:786)
at org.jboss.deployment.MainDeployer.create(MainDeployer.java:778)
at org.jboss.deployment.MainDeployer.deploy(MainDeployer.java:641)
at org.jboss.deployment.MainDeployer.deploy(MainDeployer.java:605)
at sun.reflect.GeneratedMethodAccessor20.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at org.jboss.mx.capability.ReflectedMBeanDispatcher.invoke(ReflectedMBeanDispatcher.java:284)
at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:546)
at org.jboss.mx.util.MBeanProxyExt.invoke(MBeanProxyExt.java:177)
at $Proxy6.deploy(Unknown Source)
at org.jboss.deployment.scanner.URLDeploymentScanner.deploy(URLDeploymentScanner.java:302)
at org.jboss.deployment.scanner.URLDeploymentScanner.scan(URLDeploymentScanner.java:476)
at org.jboss.deployment.scanner.AbstractDeploymentScanner$ScannerThread.doScan(AbstractDeploymentScanner.java:201)
at org.jboss.deployment.scanner.AbstractDeploymentScanner$ScannerThread.loop(AbstractDeploymentScanner.java:212)
at org.jboss.deployment.scanner.AbstractDeploymentScanner$ScannerThread.run(AbstractDeploymentScanner.java:191)
15:00:41,075 INFO [EjbModule] Deploying Chap2EchoInfoBean
15:00:41,652 INFO [EjbModule] Deploying chap2.EchoBean
15:00:42,107 INFO [EjbMBeanAdaptor] Don't panic, just a stack trace
java.lang.Throwable: getMBeanInfo trace
at org.jboss.chap2.ex3.EjbMBeanAdaptor.getMBeanInfo(EjbMBeanAdaptor.java:153)
at org.jboss.mx.server.MBeanServerImpl.getMBeanInfo(MBeanServerImpl.java:568)
at org.jboss.system.ServiceController.getServiceProxy(ServiceController.java:745)
at org.jboss.system.ServiceController.create(ServiceController.java:276)
at org.jboss.system.ServiceController.create(ServiceController.java:243)
at org.jboss.system.ServiceController.create(ServiceController.java:333)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at org.jboss.mx.capability.ReflectedMBeanDispatcher.invoke(ReflectedMBeanDispatcher.java:284)
at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:546)
at org.jboss.mx.util.MBeanProxyExt.invoke(MBeanProxyExt.java:177)
at $Proxy31.create(Unknown Source)
at org.jboss.ejb.EjbModule.createService(EjbModule.java:301)
at org.jboss.system.ServiceMBeanSupport.create(ServiceMBeanSupport.java:158)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at org.jboss.mx.capability.ReflectedMBeanDispatcher.invoke(ReflectedMBeanDispatcher.java:284)
at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:546)
at org.jboss.system.ServiceController$ServiceProxy.invoke(ServiceController.java:976)
at $Proxy14.create(Unknown Source)
at org.jboss.system.ServiceController.create(ServiceController.java:310)
at org.jboss.system.ServiceController.create(ServiceController.java:243)
at sun.reflect.GeneratedMethodAccessor4.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at org.jboss.mx.capability.ReflectedMBeanDispatcher.invoke(ReflectedMBeanDispatcher.java:284)
at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:546)
at org.jboss.mx.util.MBeanProxyExt.invoke(MBeanProxyExt.java:177)
at $Proxy12.create(Unknown Source)
at org.jboss.ejb.EJBDeployer.create(EJBDeployer.java:523)
at org.jboss.deployment.MainDeployer.create(MainDeployer.java:786)
at org.jboss.deployment.MainDeployer.create(MainDeployer.java:778)
at org.jboss.deployment.MainDeployer.deploy(MainDeployer.java:641)
at org.jboss.deployment.MainDeployer.deploy(MainDeployer.java:605)
at sun.reflect.GeneratedMethodAccessor20.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at org.jboss.mx.capability.ReflectedMBeanDispatcher.invoke(ReflectedMBeanDispatcher.java:284)
at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:546)
at org.jboss.mx.util.MBeanProxyExt.invoke(MBeanProxyExt.java:177)
at $Proxy6.deploy(Unknown Source)
at org.jboss.deployment.scanner.URLDeploymentScanner.deploy(URLDeploymentScanner.java:302)
at org.jboss.deployment.scanner.URLDeploymentScanner.scan(URLDeploymentScanner.java:476)
at org.jboss.deployment.scanner.AbstractDeploymentScanner$ScannerThread.doScan(AbstractDeploymentScanner.java:201)
at org.jboss.deployment.scanner.AbstractDeploymentScanner$ScannerThread.loop(AbstractDeploymentScanner.java:212)
at org.jboss.deployment.scanner.AbstractDeploymentScanner$ScannerThread.run(AbstractDeploymentScanner.java:191)
15:00:42,693 INFO [EjbMBeanAdaptor] Don't panic, just a stack trace
java.lang.Throwable: getMBeanInfo trace
at org.jboss.chap2.ex3.EjbMBeanAdaptor.getMBeanInfo(EjbMBeanAdaptor.java:153)
at org.jboss.mx.server.MBeanServerImpl.getMBeanInfo(MBeanServerImpl.java:568)
at org.jboss.system.ServiceController.getServiceProxy(ServiceController.java:745)
at org.jboss.system.ServiceController.create(ServiceController.java:276)
at org.jboss.system.ServiceController.create(ServiceController.java:243)
at org.jboss.system.ServiceController.create(ServiceController.java:333)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at org.jboss.mx.capability.ReflectedMBeanDispatcher.invoke(ReflectedMBeanDispatcher.java:284)
at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:546)
at org.jboss.mx.util.MBeanProxyExt.invoke(MBeanProxyExt.java:177)
at $Proxy31.create(Unknown Source)
at org.jboss.ejb.EjbModule.createService(EjbModule.java:301)
at org.jboss.system.ServiceMBeanSupport.create(ServiceMBeanSupport.java:158)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at org.jboss.mx.capability.ReflectedMBeanDispatcher.invoke(ReflectedMBeanDispatcher.java:284)
at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:546)
at org.jboss.system.ServiceController$ServiceProxy.invoke(ServiceController.java:976)
at $Proxy14.create(Unknown Source)
at org.jboss.system.ServiceController.create(ServiceController.java:310)
at org.jboss.system.ServiceController.create(ServiceController.java:243)
at sun.reflect.GeneratedMethodAccessor4.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at org.jboss.mx.capability.ReflectedMBeanDispatcher.invoke(ReflectedMBeanDispatcher.java:284)
at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:546)
at org.jboss.mx.util.MBeanProxyExt.invoke(MBeanProxyExt.java:177)
at $Proxy12.create(Unknown Source)
at org.jboss.ejb.EJBDeployer.create(EJBDeployer.java:523)
at org.jboss.deployment.MainDeployer.create(MainDeployer.java:786)
at org.jboss.deployment.MainDeployer.create(MainDeployer.java:778)
at org.jboss.deployment.MainDeployer.deploy(MainDeployer.java:641)
at org.jboss.deployment.MainDeployer.deploy(MainDeployer.java:605)
at sun.reflect.GeneratedMethodAccessor20.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at org.jboss.mx.capability.ReflectedMBeanDispatcher.invoke(ReflectedMBeanDispatcher.java:284)
at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:546)
at org.jboss.mx.util.MBeanProxyExt.invoke(MBeanProxyExt.java:177)
at $Proxy6.deploy(Unknown Source)
at org.jboss.deployment.scanner.URLDeploymentScanner.deploy(URLDeploymentScanner.java:302)
at org.jboss.deployment.scanner.URLDeploymentScanner.scan(URLDeploymentScanner.java:476)
at org.jboss.deployment.scanner.AbstractDeploymentScanner$ScannerThread.doScan(AbstractDeploymentScanner.java:201)
at org.jboss.deployment.scanner.AbstractDeploymentScanner$ScannerThread.loop(AbstractDeploymentScanner.java:212)
at org.jboss.deployment.scanner.AbstractDeploymentScanner$ScannerThread.run(AbstractDeploymentScanner.java:191)
15:00:42,761 INFO [EjbMBeanAdaptor] Begin invoke, actionName=create
15:00:42,768 INFO [EjbMBeanAdaptor] Notified of create state
15:00:42,769 INFO [EjbMBeanAdaptor] End invoke, actionName=create
15:00:42,780 INFO [EjbMBeanAdaptor] Don't panic, just a stack trace
java.lang.Throwable: getMBeanInfo trace
at org.jboss.chap2.ex3.EjbMBeanAdaptor.getMBeanInfo(EjbMBeanAdaptor.java:153)
at org.jboss.mx.server.MBeanServerImpl.getMBeanInfo(MBeanServerImpl.java:568)
at org.jboss.mx.util.JMXInvocationHandler.<init>(JMXInvocationHandler.java:66)
at org.jboss.mx.util.MBeanProxy.get(MBeanProxy.java:79)
at org.jboss.mx.util.MBeanProxy.get(MBeanProxy.java:67)
at org.jboss.management.j2ee.MBean.postCreation(MBean.java:154)
at org.jboss.management.j2ee.J2EEManagedObject.postRegister(J2EEManagedObject.java:250)
at org.jboss.mx.server.registry.BasicMBeanRegistry.registerMBean(BasicMBeanRegistry.java:278)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at org.jboss.mx.capability.ReflectedMBeanDispatcher.invoke(ReflectedMBeanDispatcher.java:284)
at org.jboss.mx.interceptor.ObjectReferenceInterceptor.invoke(ObjectReferenceInterceptor.java:59)
at org.jboss.mx.interceptor.MBeanAttributeInterceptor.invoke(MBeanAttributeInterceptor.java:43)
at org.jboss.mx.interceptor.PersistenceInterceptor2.invoke(PersistenceInterceptor2.java:93)
at org.jboss.mx.server.MBeanInvoker.invoke(MBeanInvoker.java:76)
at javax.management.modelmbean.RequiredModelMBean.invoke(RequiredModelMBean.java:144)
at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:546)
at org.jboss.mx.server.MBeanServerImpl.registerMBean(MBeanServerImpl.java:997)
at org.jboss.mx.server.MBeanServerImpl.registerMBean(MBeanServerImpl.java:327)
at org.jboss.management.j2ee.MBean.create(MBean.java:65)
at org.jboss.management.j2ee.factory.ServiceModuleFactory.create(ServiceModuleFactory.java:53)
at org.jboss.management.j2ee.LocalJBossServerDomain.handleNotification(LocalJBossServerDomain.java:383)
at org.jboss.mx.server.NotificationListenerProxy.handleNotification(NotificationListenerProxy.java:69)
at javax.management.NotificationBroadcasterSupport.sendNotification(NotificationBroadcasterSupport.java:93)
at org.jboss.deployment.SubDeployerSupport.start(SubDeployerSupport.java:178)
at org.jboss.deployment.SARDeployer.start(SARDeployer.java:229)
at org.jboss.deployment.MainDeployer.start(MainDeployer.java:832)
at org.jboss.deployment.MainDeployer.start(MainDeployer.java:824)
at org.jboss.deployment.MainDeployer.deploy(MainDeployer.java:642)
at org.jboss.deployment.MainDeployer.deploy(MainDeployer.java:605)
at sun.reflect.GeneratedMethodAccessor20.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at org.jboss.mx.capability.ReflectedMBeanDispatcher.invoke(ReflectedMBeanDispatcher.java:284)
at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:546)
at org.jboss.mx.util.MBeanProxyExt.invoke(MBeanProxyExt.java:177)
at $Proxy6.deploy(Unknown Source)
at org.jboss.deployment.scanner.URLDeploymentScanner.deploy(URLDeploymentScanner.java:302)
at org.jboss.deployment.scanner.URLDeploymentScanner.scan(URLDeploymentScanner.java:476)
at org.jboss.deployment.scanner.AbstractDeploymentScanner$ScannerThread.doScan(AbstractDeploymentScanner.java:201)
at org.jboss.deployment.scanner.AbstractDeploymentScanner$ScannerThread.loop(AbstractDeploymentScanner.java:212)
at org.jboss.deployment.scanner.AbstractDeploymentScanner$ScannerThread.run(AbstractDeploymentScanner.java:191)
15:00:42,979 INFO [EjbMBeanAdaptor] Begin invoke, actionName=getState
15:00:45,778 INFO [Chap2EchoInfoBean] Created table 'CHAP2ECHOINFOBEAN' successfully.
15:00:45,924 INFO [EntityInstancePool] Started jboss.j2ee:jndiName=local/Chap2EchoInfoBean,plugin=pool,service=EJB
15:00:45,929 INFO [EntityContainer] Started jboss.j2ee:jndiName=local/Chap2EchoInfoBean,service=EJB
15:00:46,025 INFO [StatelessSessionInstancePool] Started jboss.j2ee:jndiName=local/chap2.EchoBean,plugin=pool,service=EJB
15:00:46,030 INFO [StatelessSessionContainer] Started jboss.j2ee:jndiName=local/chap2.EchoBean,service=EJB
15:00:46,033 INFO [EjbMBeanAdaptor] Begin invoke, actionName=start
15:00:46,037 INFO [EjbMBeanAdaptor] Notified of start state
15:00:46,051 INFO [EjbMBeanAdaptor] Testing Echo
15:00:46,313 INFO [EchoBean] echo, info=echo info, arg=, arg=startService
15:00:46,319 INFO [EjbMBeanAdaptor] echo(startService) = startService
15:00:46,366 INFO [EjbMBeanAdaptor] Started null
15:00:46,370 INFO [EjbMBeanAdaptor] End invoke, actionName=start
15:00:46,372 INFO [EjbModule] Started jboss.j2ee:module=chap2-ex3.jar,service=EjbModule
15:00:46,375 INFO [EJBDeployer] Deployed: file:/private/tmp/jboss-3.2.3/server/default/tmp/deploy/tmp58329chap2-ex3.ear-contents/chap2-ex3.jar
15:00:46,599 INFO [EARDeployer] Started J2EE application: file:/private/tmp/jboss-3.2.3/server/default/deploy/chap2-ex3.ear
15:00:46,648 INFO [MainDeployer] Deployed package: file:/private/tmp/jboss-3.2.3/server/default/deploy/chap2-ex3.ear
The stack traces are not exceptions. They are traces coming from line 150 of the EjbMBeanAdaptor code to demonstrate that clients ask for the MBean interface when they want to discover the MBean's capabilities. Notice that the EJB container (lines with [EjbModule]) is started before the example MBean (lines with [EjbMBeanAdaptor]).
Now, let's invoke the echo method using the JMX console web application. Browse to http://localhost:8080/jmx-console/HtmlAdaptor?action=inspectMBean&name=jboss.book%3Aservice%3DEjbMBeanAdaptor and scroll down to the echo operation section. The view should be like that shown in See The EjbMBeanAdaptor MBean operations JMX console view..
As shown, we have already entered an argument string of "-echo-arg" into the ParamValue text field. Press the Invoke button and a result string of "AdaptorPrefix-echo-arg" is displayed on the results page. The server console will show several stack traces from the various metadata queries issues by the JMX console and the MBean invoke method debugging lines:
10:51:48,671 INFO [EjbMBeanAdaptor] Begin invoke, actionName=echo
10:51:48,671 INFO [EjbMBeanAdaptor] Lookup EchoLocalHome@local/chap2.EchoBean
10:51:48,687 INFO [EchoBean] echo, info=echo info, arg=, arg=-echo-arg
10:51:48,687 INFO [EjbMBeanAdaptor] Result: AdaptorPrefix-echo-arg
10:51:48,687 INFO [EjbMBeanAdaptor] End invoke, actionName=echo
JBoss has an extensible deployment architecture that allows one to incorporate components into the bare JBoss JMX microkernel. See The deployment layer classes. shows the classes in the deployment layer.
The MainDeployer is the deployment entry point. Requests to deploy a component are sent to the MainDeployer and it determines if there is a subdeployer capable of handling the deployment, and if there is, it delegates the deployment to the subdeployer. We saw an example of this when we looked at how the MainDeployer used the SARDeployer to deploy MBean services. The current deployers included with JBoss are:
The MainDeployer , JARDeployer and SARDeployer are hard coded deployers in the JBoss server core. The AbstractWebContainer , EARDeployer , EJBDeployer , and RARDeployer are MBean services that register themselves as deployers with the MainDeployer using the addDeployer(SubDeployer) operation. The SubDeployer interface is given in See The org.jboss.deployment.SubDeployer interface.:
* The <code>accepts</code> method is called by MainDeployer to
* determine which deployer is suitable for a DeploymentInfo.
* @param sdi a <code>DeploymentInfo</code> value
* @return a <code>boolean</code> value
boolean accepts(DeploymentInfo sdi);
* The <code>init</code> method lets the deployer set a few properties
* of the DeploymentInfo, such as the watch url.
* @param sdi a <code>DeploymentInfo</code> value
* @throws DeploymentException if an error occurs
void init(DeploymentInfo sdi) throws DeploymentException;
* Set up the components of the deployment that do not
* @param sdi a <code>DeploymentInfo</code> value
* @throws DeploymentException Failed to deploy
void create(DeploymentInfo sdi) throws DeploymentException;
* The <code>start</code> method sets up relationships with other components.
* @param sdi a <code>DeploymentInfo</code> value
* @throws DeploymentException if an error occurs
void start(DeploymentInfo sdi) throws DeploymentException;
* The <code>stop</code> method removes relationships between components.
* @param sdi a <code>DeploymentInfo</code> value
* @throws DeploymentException if an error occurs
void stop(DeploymentInfo sdi) throws DeploymentException;
* The <code>destroy</code> method removes individual components
* @param sdi a <code>DeploymentInfo</code> value
* @throws DeploymentException if an error occurs
void destroy(DeploymentInfo sdi) throws DeploymentException;
The DeploymentInfo object is basically a data structure that encapsulates the complete state of a deployable component. When the MainDeployer receives a deployment request, it iterates through its registered subdeployers and invokes the accepts(DeploymentInfo) method on the subdeployer. The first subdeployer to return true is chosen and the deployment deployer and the MainDeployer will delegate the init , create , start , stop and destroy deployment life cycle operations to the subdeployer.
Deployers are the mechanism by which components are brought into a JBoss server. Deployers are also the creators of the majority of UCL instances, and the primary creator is the MainDeployer . The MainDeployer creates the UCL for a deployment early on during its init method. The UCL is created by calling the DeploymentInfo.createClassLoaders() method. As of the 3.0.5RC1 release, only the topmost DeploymentInfo will actually create a UCL. All subdeployments will add their class paths to their parent DeploymentInfo UCL. Previously every subdeployment created a UCL for its deployment, and a seperate UCL for every manifest or classpath reference. This could cause problems because classes ended up being loaded by more than one UCL and IllegalAccessError s and LinkageError s would result. Every deployment does have a standalone URLClassLoader that uses the deployment URL as its path. This is used to localize the loading of resources such as deployment descriptors. See An illustration of the class loaders involved with an EAR deployment. provides an illustration of the interaction between Deployer s, DeploymentInfo s and class loaders.
The figure illustrates an EAR deployment with EJB and WAR subdeployments. The EJB deployment references the lib/util.jar utility jar via its manifest. The WAR includes classes in its WEB-INF/classes directory as well as the WEB-INF/lib/jbosstest-web-util.jar. Each deployment has a DeploymentInfo instance that has a URLClassLoader pointing to the deployment archive. The DeploymentInfo associated with some.ear is the only one to have a UCL created. The ejbs.jar and web.war DeploymentInfo s add their deployment archive to the some.ear UCL classpath, and share this UCL as their deployemnt UCL. The EJBDeployer also adds any manifest jars to the EAR UCL.
The WARDeployer behaves differently than other deployers in that it only adds its WAR archive to the DeploymentInfo UCL classpath. The loading of classes from the WAR WEB-INF/classes and WEB-INF/lib locations is handled by the servlet container class loader. The servlet container class loaders delegate to the WAR DeploymentInfo UCL as their parent class loader, but the server container class loader is not part of the JBoss class loader repository. Therefore, classes inside of a WAR are not visible to other components. Classes that need to be shared between web application components and other components such as EJBs, and MBeans need to be loaded into the shared class loader repository either by including the classes into a SAR or EJB deployment, or by referencing a jar containing the shared classes through a manifest Class-Path entry. In the case of a SAR, the SAR classpath element in the service deployment serves the same purpose as a jar manifest Class-Path.
3.2.2 added an snmp-adaptor service that can be used to intercept JMX notifications emitted by MBeans, convert them to traps and send them to SNMP managers. In this respect the snmp-adaptor acts as a SNMP agent. Future versions may offer support for full agent get/set functionality that maps onto MBean attributes or operations.
It can be used to integrate JBoss with higher order system/network management platforms (e.g., HP OpenView), thus making the MBeans visible to those systems. The MBean developer can instrument the MBeans by producing notifications for any significant event (e.g. server coldstart). The adaptor can then be configured to intercept and map those notifications to SNMP traps. The adaptor uses the JoeSNMP package from OpenNMS as the SNMP engine.
SnmpAgentService is the main MBean that implements the SNMP agent. It is configured by means of three different configuration files:
The org.jboss.jmx.adaptor.snmp.agent.SnmpAgentService allows one to send V1 or V2 SNMP traps to one or more SNMP managers defined by their IP address, listening port number and expected SNMP version.
org.jboss.jmx.adaptor.snmp.trapd.TrapdService is a simple MBean that acts as an SNMP Manager. It listens to a configurable port for incoming traps and logs them as DEBUG messages using the system logger. You can modify the log4j configuration to redirect the log output to a file. SnmpAgentService and TrapdService are not dependent with each other.
In addition to the MBean services notion that allows for the ability to integrate arbitrary functionality, JBoss also has a detached invoker concept that allows MBean services to expose functional interfaces via arbitrary protocols for remote access by clients. This notion first showed up in 3.0 for the EJB container and it has been further generalized to any MBean service in 3.2. The notion of a detached invoker is that remoting and the protocol by which a service is accessed is a functional aspect or service from independent of the component. Thus, one can make a naming service available for use via RMI/JRMP, RMI/HTTP, RMI/SOAP, or any arbitrary custom transport.
Let's begin our discussion of the detached invoker architecture with an overview of the components involved. The main components in the detached invoker architecture are shown in See The main components in the detached invoker architecture..
On the client side, there exist a client proxy which exposes the interface(s) of the MBean service. This is the same smart, compile-less dynamic proxy that we use for EJB home and remote interfaces. The only difference between the proxy for an arbitrary service and the EJB is the set of interfaces exposed as well as the client side interceptors found inside the proxy. The client interceptors are represented by the rectangles found inside of the client proxy. An interceptor is an assembly line type of pattern that allows for transformation of a method invocation and/or return values. A client obtains a proxy through some lookup mechanism, typically JNDI. Although RMI is indicated in See The main components in the detached invoker architecture., the only real requirement on the exposed interface and its types is that they are serializable between the client server over JNDI as well as the transport layer.
The choice of the transport layer is determined by the last interceptor in the client proxy, which is referred to as the "Invoker Interceptor" in See The main components in the detached invoker architecture.. The invoker interceptor contains a reference to the transport specific stub of the server side "Detached Invoker" MBean service. The invoker interceptor also handles the optimization of calls that occur within the same VM as the target MBean. When the invoker interceptor detects that this is the case the call is passed to a call-by-reference invoker that simply passes the invocation along to the target MBean.
The detached invoker service is responsible for making a generic invoke operation available via the transport the detached invoker handles. The generic invoke operation signature is given in See The Invoker interface. where the org.jboss.invocation.Invoker interface is shown.
import org.jboss.proxy.Interceptor;
import org.jboss.util.id.GUID;
Object invoke(Invocation invocation) throws Exception;
The Invoker interface extends Remote to be compatible with RMI, but this does not mean that an invoker must expose an RMI service stub. The detached invoker service simply acts as a transport gateway that accepts invocations represented as the org.jboss.invocation.Invocation object over its specific transport, unmarshalls the invocation, forwards the invocation onto the destination MBean service, represented by the "Target MBean" in See The main components in the detached invoker architecture., and marshalls the return value or exception resulting from the forwarded call back to the client.
The Invocation object is just a representation of a method invocation context. This includes the target MBean name, the method, the method arguments, a context of information associated with the proxy by the proxy factory, and an arbitrary map of data associated with the invocation by the client proxy interceptors.See The Invocation class used to represent method invocations. shows the key methods of the Invocation class.
import java.lang.reflect.Method;
import java.security.Principal;
import javax.transaction.Transaction;
/** The signature of the invoke() method */
public static final String[] INVOKE_SIGNATURE = {"org.jboss.invocation.Invocation"};
* Contextual information to the invocation that is not part of the payload.
* as_is classes that will not be marshalled by the invocation
* (java.* and javax.* or anything in system classpath is OK)
/** Payload will be marshalled for type hiding at the RMI layers. */
protected InvocationContext invocationContext;
as_is_payload = new HashMap();
transient_payload = new HashMap();
public Invocation( Object id, Method m, Object[] args, Transaction tx,
Principal identity, Object credential )
this.as_is_payload = new HashMap();
this.transient_payload = new HashMap();
public void setValue(Object key, Object value)
setValue(key, value, PayloadKey.PAYLOAD);
public void setValue(Object key, Object value, PayloadKey type)
if(type == PayloadKey.TRANSIENT)
transient_payload.put(key,value);
else if(type == PayloadKey.AS_IS)
else if(type == PayloadKey.PAYLOAD)
throw new IllegalArgumentException("Unknown PayloadKey: " + type);
public Object getValue(Object key)
Object rtn = payload.get(key);
rtn = transient_payload.get(key);
public Object getPayloadValue(Object key)
... Convience accessor methods deleted...
The configuration of the client proxy is done by the server side proxy factory MBean service, indicated by the "Proxy Factory" component in See The main components in the detached invoker architecture.. The proxy factory preforms the following tasks:
The last component in See The main components in the detached invoker architecture. is the "Target MBean" service that wishes to expose an interface for invocations to remote clients. The steps required for an MBean service to be accessible through a given interface are:
In the section on connecting to the JMX server we mentioned that there was a service that allows one to access the javax.management.MBeanServer via any protocol using an invoker service. In this section we present the org.jboss.jmx.connector.invoker.InvokerAdaptorService and its configuration for access via RMI/JRMP as an example of the steps required to provide remote access to an MBean service.
The InvokerAdaptorService is a simple MBean service that only exists to fulfill the target MBean role in the detached invoker pattern.
Let's go through the key details of this service. The InvokerAdaptorServiceMBean Standard MBean interface of the InvokerAdaptorService has a single ExportedInterface attribute and a single invoke(Invocation) operation. The ExportedInterface attribute allows customization of the type of interface the service exposes to clients. This has to be "compatible" with the MBeanServer class in terms of method name and signature as we will see. The invoke(Invocation) operation is the required entry point that target MBean services must expose to participate in the detached invoker pattern. This operation is invoked by the detached invoker services that have been configured to provide access to the InvokerAdaptorService .
Lines 54-64 of the InvokerAdaptorService build the HashMap<Long, Method> of the ExportedInterface Class using the org.jboss.invocation.MarshalledInvocation.calculateHash(Method) utility method. Because java.lang.reflect.Method instances are not serializable, a MarshalledInvocation version of the non-serializable Invocation class is used to marshall the invocation between the client and server. The MarshalledInvocation replaces the Method instances with their corresponding hash representation. On the server side, the MarshalledInvocation must be told what the hash to Method mapping is.
Line 64 creates a mapping between the InvokerAdaptorService service name and its hashCode representation. This is used by detached invokers to determine what the target MBean ObjectName of an Invocation is. When the target MBean name is store in the Invocation , its store as its hashCode because ObjectName s are relatively expensive objects to create. The org.jboss.system.Registry is a global map like construct that invokers use to store the hashCode to ObjectName mappings in.
Lines 77-93 obtain the name of the MBean on which the MBeanServer operation is being performed and lookup the ClassLoader associated with the MBean's SAR deployment. This information is available via the org.jboss.mx.server.registry.BasicMBeanRegistry , a JBoss JMX implementation specific class. It is generally necessary for an MBean to establish the correct class loading context because the detached invoker protocol layer may not have access to the class loaders needed to unmarshall the types associated with an invocation.
Lines 101-105 install the ExposedInterface class method hash to method mapping if the invocation argument is of type MarshalledInvocation. The method mapping calculated previously at lines 54-62 is used here.
Lines 107-114 perform a second mapping from the ExposedInterface Method to the matching method of the MBeanServer class. The InvokerServiceAdaptor decouples the ExposedInterface from the MBeanServer class in that it allows an arbitrary interface. This is needed on one hand because the standard java.lang.reflect.Proxy class can only proxy interfaces. It also allows one to only expose a subset of the MBeanServer methods and add transport specific exceptions like java.rmi.RemoteException to the ExposedInterface method signatures.
Line 115 dispatches the MBeanServer method invocation to the MBeanServer instance to which the InvokerAdaptorService was deployed. The server instance variable is inherited from the ServiceMBeanSupport superclass.
Lines 117-124 handle any exceptions coming from the reflective invocation including the unwrapping of any declared exception thrown by the invocation.
Line 126 is the return of the successful MBeanServer method invocation result.
Note that the InvokerAdaptorService MBean does not deal directly with any transport specific details. There is the calculation of the method hash to Method mapping, but this is a transport independent detail.
Now let's take a look at how the InvokerAdaptorService may be used to expose the same org.jboss.jmx.adaptor.rmi.RMIAdaptor interface via RMI/JRMP as seen in Connecting to JMX Using RMI. We will start by presenting the proxy factory and InvokerAdaptorService configurations found in the default setup in the jmx-invoker-adaptor-service.sar deployment. See The default jmx-invoker-adaptor-server.sar jboss-service.xml deployment descriptor. shows the jboss-service.xml descriptor for this deployment.
The first MBean, org.jboss.invocation.jrmp.server.JRMPProxyFactory , is the proxy factory MBean service that creates proxies for the RMI/JRMP protocol. The complete reference information on the JRMPProxyFactory may be found in section The JRMPProxyFactory Service - Building Dynamic JRMP Proxies. The configuration of this service as shown in See The default jmx-invoker-adaptor-server.sar jboss-service.xml deployment descriptor. states that the JRMPInvoker will be used as the detached invoker, the InvokerAdaptorService is the target mbean to which requests will be forwarded, that the proxy will expose the RMIAdaptor interface, the proxy will be bound into JNDI under the name "jmx/invoker/RMIAdaptor", and the proxy will contain 3 interceptors: ClientMethodInterceptor , InvokerAdaptorClientInterceptor , InvokerInterceptor . The configuration of the InvokerAdaptorService simply sets the RMIAdaptor interface that the service is exposing.
The last piece of the configuration for exposing the InvokerAdaptorService via RMI/JRMP is the detached invoker. The detached invoker we will use is the standard RMI/JRMP invoker used by the EJB containers for home and remote invocations, and this is the org.jboss.invocation.jrmp.server.JRMPInvoker MBean service configured in the conf/jboss-service.xml descriptor. That we can use the same service instance emphasizes the detached nature of the invokers. The JRMPInvoker simply acts as the RMI/JRMP endpoint for all RMI/JRMP proxies regardless of the interface(s) the proxies expose or the service the proxies utilize.
The org.jboss.invocation.jrmp.server.JRMPInvoker class is an MBean service that provides the RMI/JRMP implementation of the Invoker interface. The JRMPInvoker exports itself as an RMI server so that when it is used as the Invoker in a remote client, the JRMPInvoker stub is sent to the client instead and invocations use the RMI/JRMP protocol.
The JRMPInvoker MBean supports a number of attribute to configure the RMI/JRMP transport layer. Its configurable attributes are:
The org.jboss.invocation.pooled.server.PooledInvoker is an MBean service that provides RMI over a custom socket transport implementation of the Invoker interface. The PooledInvoker exports itself as an RMI server so that when it is used as the Invoker in a remote client, the PooledInvoker stub is sent to the client instead and invocations use the a custom socket protocol.
The PooledInvoker MBean supports a number of attribute to configure the socket transport layer. Its configurable attributes are:
The org.jboss.invocation.iiop.IIOPInvoker class is an MBean service that provides the RMI/IIOP implementation of the Invoker interface. The IIOPInvoker IIOP invoker that routes IIOP requests to CORBA servants are used by the This used by the org.jboss.proxy.ejb.IORFactory proxy factory to create RMI/IIOP proxies. However, rather than creating Java proxies (as the JRMP proxy factory does), this factory creates CORBA IORs. An <code>IORFactory</code> is associated to a given enterprise bean. It registers with the IIOP invoker two CORBA servants: anEjbHomeCorbaServant for the bean's EJBHome and an EjbObjectCorbaServant for the bean's EJBObjects.
The IIOPInvoker MBean has no configurable proprties, since all properties are configured from the conf/jacorb.properties property file used by the JacORB CORBA service.
The org.jboss.invocation.jrmp.server.JRMPProxyFactory MBean service is a proxy factory that can expose any interface with RMI compatible semantics for access to remote clients using JRMP as the transport.
The JRMPProxyFactory supports the following attributes:
The org.jboss.invocation.http.server.HttpInvoker MBean service provides the provides support for making invocations into the JMX bus over HTTP. Unlike the JRMPInvoker , the HttpInvoker is not an implementation of Invoker , but it does implement the Invoker.invoke method. The HttpInvoker is accessed indirectly by issuing an HTTP POST against the org.jboss.invocation.http.servlet.InvokerServlet . The HttpInvoker exports a client side proxy in the form of the org.jboss.invocation.http.interfaces.HttpInvokerProxy class, which is an implementation of Invoker , and is serializable. The HttpInvoker is a drop in replacement for the JRMPInvoker as the target of the bean-invoker and home-invoker EJB configuration elements. The HttpInvoker and InvokerServlet are deployed in the http-inovker.sar discussed in the JNDI chapter in the section entitled Accessing JNDI over HTTP
The HttpInvoker supports the following attributes:
The org.jboss.proxy.generic.ProxyFactoryHA service is an extension of the ProxyFactoryHA that is a cluster aware factory. The ProxyFactoryHA fully supports all of the attributes of the JRMPProxyFactory. This means that customized bindings of the port, interface and socket transport are available to clustered RMI/JRMP as well. In addition, the following cluster specific attributes are supported:
The RMI/HTTP layer added in JBoss-3.0.2 has been extended to allow for software load balancing of the invocations in a clustered environment in JBoss-3.0.3. An HA capable extension of the HTTP invoker has been added that borrows much of its functionality from the HA-RMI/JRMP clustering.
To enable HA-RMI/HTTP you need to configure the invokers for the EJB container. This is done through either a jboss.xml descriptor, or the standardjboss.xml descriptor. See A jboss.xml stateless session configuration for HA-RMI/HTTP. shows is an example of a stateless session configuration taken from the org.jboss.test.hello testsuite package.
The org.jboss.invocation.http.server.HttpProxyFactory MBean service is a proxy factory that can expose any interface with RMI compatible semantics for access to remote clients using HTTP as the transport.
The HttpProxyFactory supports the following attributes:
Using the HttpProxyFactory MBean and JMX, you can expose any interface for access using HTTP as the transport. The interface to expose does not have to be an RMI interface, but it does have to be compatible with RMI in that all method parameters and return values are serializable. There is also no support for converting RMI interfaces used as method parameters or return values into their stubs.
The three steps to making your object invocable via HTTP are:
import java.lang.reflect.Method;
import org.jboss.invocation.MarshalledInvocation;
HashMap marshalledInvocationMapping = new HashMap();
// Build the Naming interface method map
Method[] methods = SRPRemoteServerInterface.class.getMethods();
for(int m = 0; m < methods.length; m ++)
Long hash = new Long(MarshalledInvocation.calculateHash(method));
marshalledInvocationMapping.put(hash, method);
import org.jboss.invocation.Invocation;
import org.jboss.invocation.MarshalledInvocation;
public Object invoke(Invocation invocation) throws Exception
SRPRemoteServerInterface theServer = <the_actual_rmi_server_object>;
// Set the method hash to Method mapping
if (invocation instanceof MarshalledInvocation)
MarshalledInvocation mi = (MarshalledInvocation) invocation;
mi.setMethodMap(marshalledInvocationMapping);
// Invoke the Naming method via reflection
Method method = invocation.getMethod();
Object[] args = invocation.getArguments();
value = method.invoke(theServer, args);
catch(InvocationTargetException e)
Throwable t = e.getTargetException();
throw new UndeclaredThrowableException(t, method.toString());
<!-- Expose the SRP service interface via HTTP -->
<mbean code="org.jboss.invocation.http.server.HttpProxyFactory"
name="jboss.security.tests:service=SRP/HTTP">
<attribute name="InvokerURL">http://localhost:8080/invoker/JMXInvokerServlet
<attribute name="InvokerName">jboss.security.tests:service=SRPService
<attribute name="ExportedInterface">
org.jboss.security.srp.SRPRemoteServerInterface
<attribute name="JndiName">srp-test-http/SRPServerInterface</attribute>
Any client may now lookup the RMI interface from JNDI using the name specified in the HttpProxyFactory (e.g., srp-test-http/SRPServerInterface) and use the obtain proxy in exactly the same manner as the RMI/JRMP version.