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 Scott.Stark@jboss.org
public class ExObj implements Serializable
public ExObj2 ivar = new ExObj2();
----------------------------------------------------------------------------
* @author Scott.Stark@jboss.org
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 scott.stark@jboss.org .
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..