CHAPTER 2 The JBoss JMX Microkernel

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.

JMX

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.

.

FIGURE 2-1. The JBoss JMX integration bus and the standard JBoss components

An Introduction to 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:

  • Instrumentation, which are the resources to manage
  • Agents, which are the controllers of the instrumentation level objects
  • Distributed services, the mechanism by which administration applications interact with agents and their managed objects

 

FIGURE 2-2. The Relationship between the components of the JMX architecture

Instrumentation Level

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.

Agent Level

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.

Distributed Services Level

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.

  • Provide an interface for management applications to interact transparently with an agent and its JMX manageable resources through a connector
  • Exposes a management view of a JMX agent and its MBeans by mapping their semantic meaning into the constructs of a data-rich protocol (for example HTML or SNMP)
  • Distributes management information from high-level management platforms to numerous JMX agents
  • Consolidates management information coming from numerous JMX agents into logical views that are relevant to the end user's business operations
  • Provides security

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.

JMX Component Overview

This section offers an overview of the instrumentation and agent level components. The instrumentation level components include the following:

  • MBeans (standard, dynamic, open, and model MBeans)
  • Notification model elements
  • MBean metadata classes

The agent level components include:

  • MBean server
  • Agent services
Managed Beans or MBeans

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:

  • Attributes values that may be accessed by name
  • Operations or functions that may be invoked
  • Notifications or events that may be emitted
  • The constructors for the MBean's Java class

JMX defines four types of MBeans to support different instrumentation needs:

  • Standard MBeans These use a simple JavaBean style naming convention and a statically defined management interface. This is currently the most common type of MBean used by JBoss.
  • Dynamic MBeans These must implement the javax.management.DynamicMBean interface, and they expose their management interface at runtime when the component is instantiated for the greatest flexibility. JBoss makes use of Dynamic MBeans in circumstances where the components to be managed are not known until runtime.
  • Open MBeans These are an extension of dynamic MBeans. Open MBeans rely on basic, self-describing, user-friendly data types for universal manageability. JBoss 3.0.6+ and 3.2+ implement the JMX 1.1 OpenMBeans.
  • Model MBeans These are also an extension of dynamic MBeans. Model MBeans must implement the javax.management.modelmbean.ModelMBean interface. Model MBeans simplify the instrumentation of resources by providing default behavior. Although JBoss does not use any Model MBeans for its core services as of the 3.2.0 release, there is a Model MBean implementation known as an XMBean.

We will present an example of a Standard and a Model MBean in the section that discusses extending JBoss with your own custom services.

Notification Model

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.

MBean Metadata Classes

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.

MBean Server

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:

  • Another MBean
  • The agent itself
  • A remote management application (through the distributed services)

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:

  • Discovering the management interface of MBeans
  • Reading and writing attribute values
  • Invoking operations defined by MBeans
  • Registering for notifications events
  • Querying MBeans based on their object name or their attribute values

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.

Agent Services

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:

  • A Dynamic class loading MLet (management applet) service. This allows for the retrieval and instantiation of new classes and native libraries from an arbitrary network location.
  • Monitor services. These observe an MBean attribute's numerical or string value, and can notify other objects of several types of changes in the target.
  • Timer services. These provide a scheduling mechanism based on a one-time alarm-clock notification or on a repeated, periodic notification.
  • The relation service. This service defines associations between MBeans and enforces consistency on the relationships.

Any JMX-compliant implementation will provide all of these agent services. JBoss currently does not rely on any of these standard agent services.

JBoss JMX Implementation Architecture

The JBoss ClassLoader Architecture

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 and Types in Java

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.

ClassCastExceptions - I'm Not Your Type

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..

The ExCCEc class used to demonstrate ClassCastException due to duplicate class loaders
  1. package org.jboss.chap2.ex0;
  2.  
  3. import java.io.File;
  4. import java.net.URL;
  5. import java.net.URLClassLoader;
  6. import java.lang.reflect.Method;
  7.  
  8. import org.apache.log4j.Logger;
  9.  
  10. import org.jboss.util.ChapterExRepository;
  11. import org.jboss.util.Debug;
  12.  
  13. /** An example of a ClassCastException that results from classes loaded through
  14. * different class loaders.
  15. * @author Scott.Stark@jboss.org
  16. * @version $Revision:$
  17. */
  18. public class ExCCEc
  19. {
  20. public static void main(String[] args) throws Exception
  21. {
  22. ChapterExRepository.init(ExCCEc.class);
  23.  
  24. String chapDir = System.getProperty("chapter.dir");
  25. Logger ucl0Log = Logger.getLogger("UCL0");
  26. File jar0 = new File(chapDir+"/j0.jar");
  27. ucl0Log.info("jar0 path: "+jar0.toString());
  28. URL[] cp0 = {jar0.toURL()};
  29. URLClassLoader ucl0 = new URLClassLoader(cp0);
  30. Thread.currentThread().setContextClassLoader(ucl0);
  31. Class objClass = ucl0.loadClass("org.jboss.chap2.ex0.ExObj");
  32. StringBuffer buffer = new StringBuffer("ExObj Info");
  33. Debug.displayClassInfo(objClass, buffer, false);
  34. ucl0Log.info(buffer.toString());
  35. Object value = objClass.newInstance();
  36.  
  37. File jar1 = new File(chapDir+"/j0.jar");
  38. Logger ucl1Log = Logger.getLogger("UCL1");
  39. ucl1Log.info("jar1 path: "+jar1.toString());
  40. URL[] cp1 = {jar1.toURL()};
  41. URLClassLoader ucl1 = new URLClassLoader(cp1);
  42. Thread.currentThread().setContextClassLoader(ucl1);
  43. Class ctxClass2 = ucl1.loadClass("org.jboss.chap2.ex0.ExCtx");
  44. buffer.setLength(0);
  45. buffer.append("ExCtx Info");
  46. Debug.displayClassInfo(ctxClass2, buffer, false);
  47. ucl1Log.info(buffer.toString());
  48. Object ctx2 = ctxClass2.newInstance();
  49.  
  50. try
  51. {
  52. Class[] types = {Object.class};
  53. Method useValue = ctxClass2.getMethod("useValue", types);
  54. Object[] margs = {value};
  55. useValue.invoke(ctx2, margs);
  56. }
  57. catch(Exception e)
  58. {
  59. ucl1Log.error("Failed to invoke ExCtx.useValue", e);
  60. throw e;
  61. }
  62. }
  63. }
The ExCtx, ExObj, and ExObj2 classes used by the examples
  1. package org.jboss.chap2.ex0;
  2.  
  3. import java.io.IOException;
  4.  
  5. import org.apache.log4j.Logger;
  6.  
  7. import org.jboss.util.Debug;
  8.  
  9. /** A classes used to demonstrate various class loading issues
  10. * @author Scott.Stark@jboss.org
  11. * @version $Revision: 1.2 $
  12. */
  13. public class ExCtx
  14. {
  15. ExObj value;
  16.  
  17. public ExCtx() throws IOException
  18. {
  19. value = new ExObj();
  20. Logger log = Logger.getLogger(ExCtx.class);
  21. StringBuffer buffer = new StringBuffer("ctor.ExObj");
  22. Debug.displayClassInfo(value.getClass(), buffer, false);
  23. log.info(buffer.toString());
  24. ExObj2 obj2 = value.ivar;
  25. buffer.setLength(0);
  26. buffer = new StringBuffer("ctor.ExObj.ivar");
  27. Debug.displayClassInfo(obj2.getClass(), buffer, false);
  28. log.info(buffer.toString());
  29. }
  30. public Object getValue()
  31. {
  32. return value;
  33. }
  34. public void useValue(Object obj) throws Exception
  35. {
  36. Logger log = Logger.getLogger(ExCtx.class);
  37. StringBuffer buffer = new StringBuffer("useValue2.arg class");
  38. Debug.displayClassInfo(obj.getClass(), buffer, false);
  39. log.info(buffer.toString());
  40. buffer.setLength(0);
  41. buffer.append("useValue2.ExObj class");
  42. Debug.displayClassInfo(ExObj.class, buffer, false);
  43. log.info(buffer.toString());
  44. ExObj ex = (ExObj) obj;
  45. }
  46. void pkgUseValue(Object obj) throws Exception
  47. {
  48. Logger log = Logger.getLogger(ExCtx.class);
  49. log.info("In pkgUseValue");
  50. }
  51. }
  52.  
The ExObj and ExObj2 classes used in the examples

package org.jboss.chap2.ex0;

 

import java.io.Serializable;

 

/**

* @author Scott.Stark@jboss.org

* @version $Revision:$

*/

public class ExObj implements Serializable

{

public ExObj2 ivar = new ExObj2();

}

----------------------------------------------------------------------------

package org.jboss.chap2.ex0;

 

import java.io.Serializable;

 

/**

* @author Scott.Stark@jboss.org

* @version $Revision: 1.1$

*/

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

Buildfile: build.xml

...

[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)

[java] ... 5 more

[java] Java Result: 1

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..

The chap2-ex0c.log debugging output for the ExObj classes seen

[INFO,UCL0] ExObj Info

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>)

Implemented Interfaces:

++interface java.io.Serializable(7934ad)

++++ClassLoader: null

++++Null CodeSource

 

[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>)

Implemented Interfaces:

++interface java.io.Serializable(7934ad)

++++ClassLoader: null

++++Null CodeSource

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".

IllegalAccessException - Doing what you should not

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 class used to demonstrate IllegalAccessException due to duplicate class loaders
  1. package org.jboss.chap2.ex0;
  2.  
  3. import java.io.File;
  4. import java.net.URL;
  5. import java.net.URLClassLoader;
  6. import java.lang.reflect.Method;
  7.  
  8. import org.apache.log4j.Logger;
  9.  
  10. import org.jboss.util.ChapterExRepository;
  11. import org.jboss.util.Debug;
  12.  
  13. /** An example of IllegalAccessExceptions due to classes loaded by two class
  14. * loaders.
  15. * @author Scott.Stark@jboss.org
  16. * @version $Revision: 1.2$
  17. */
  18. public class ExIAEd
  19. {
  20. public static void main(String[] args) throws Exception
  21. {
  22. ChapterExRepository.init(ExIAEd.class);
  23.  
  24. String chapDir = System.getProperty("chapter.dir");
  25. Logger ucl0Log = Logger.getLogger("UCL0");
  26. File jar0 = new File(chapDir+"/j0.jar");
  27. ucl0Log.info("jar0 path: "+jar0.toString());
  28. URL[] cp0 = {jar0.toURL()};
  29. URLClassLoader ucl0 = new URLClassLoader(cp0);
  30. Thread.currentThread().setContextClassLoader(ucl0);
  31.  
  32. StringBuffer buffer = new StringBuffer("ExIAEd Info");
  33. Debug.displayClassInfo(ExIAEd.class, buffer, false);
  34. ucl0Log.info(buffer.toString());
  35.  
  36. Class ctxClass1 = ucl0.loadClass("org.jboss.chap2.ex0.ExCtx");
  37. buffer.setLength(0);
  38. buffer.append("ExCtx Info");
  39. Debug.displayClassInfo(ctxClass1, buffer, false);
  40. ucl0Log.info(buffer.toString());
  41. Object ctx0 = ctxClass1.newInstance();
  42.  
  43. try
  44. {
  45. Class[] types = {Object.class};
  46. Method useValue = ctxClass1.getDeclaredMethod("pkgUseValue", types);
  47. Object[] margs = {null};
  48. useValue.invoke(ctx0, margs);
  49. }
  50. catch(Exception e)
  51. {
  52. ucl0Log.error("Failed to invoke ExCtx.pkgUseValue", e);
  53. }
  54. }
  55. }

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

Buildfile: build.xml

...

[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:

[INFO,UCL0] ExIAEd Info

org.jboss.chap2.ex0.ExIAEd(65855a).ClassLoader=sun.misc.Launcher$AppClassLoader@3f52a5

..sun.misc.Launcher$AppClassLoader@3f52a5

...

[INFO,UCL0] ExCtx Info

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.

LinkageErrors - Making Sure You Are Who You Say You Are

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..

Classes demonstrating the need for loading constraints

class

{

void f()

{

x = .g();

x.secret_value = 1; // Should not be allowed

}

}

----------------------------------------------------------------------------

class

{

static g() {...}

}

----------------------------------------------------------------------------

class

{

public int secret_value;

}

----------------------------------------------------------------------------

class

{

private int secret_value;

}

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.

A concrete example of a LinkageError
  1. package org.jboss.chap2.ex0;
  2.  
  3. import java.io.File;
  4. import java.net.URL;
  5.  
  6. import org.apache.log4j.Logger;
  7. import org.jboss.util.ChapterExRepository;
  8. import org.jboss.util.Debug;
  9.  
  10. /** An example of a LinkageError due to classes being defined by more than
  11. * one class loader in a non-standard class loading environment.
  12. * @author Scott.Stark@jboss.org
  13. * @version $Revision: 1.1$
  14. */
  15. public class ExLE
  16. {
  17. public static void main(String[] args) throws Exception
  18. {
  19. ChapterExRepository.init(ExLE.class);
  20.  
  21. String chapDir = System.getProperty("chapter.dir");
  22. Logger ucl0Log = Logger.getLogger("UCL0");
  23. File jar0 = new File(chapDir+"/j0.jar");
  24. ucl0Log.info("jar0 path: "+jar0.toString());
  25. URL[] cp0 = {jar0.toURL()};
  26. Ex0URLClassLoader ucl0 = new Ex0URLClassLoader(cp0);
  27. Thread.currentThread().setContextClassLoader(ucl0);
  28. Class ctxClass1 = ucl0.loadClass("org.jboss.chap2.ex0.ExCtx");
  29. Class obj2Class1 = ucl0.loadClass("org.jboss.chap2.ex0.ExObj2");
  30. StringBuffer buffer = new StringBuffer("ExCtx Info");
  31. Debug.displayClassInfo(ctxClass1, buffer, false);
  32. ucl0Log.info(buffer.toString());
  33. buffer.setLength(0);
  34. buffer.append("ExObj2 Info, UCL0");
  35. Debug.displayClassInfo(obj2Class1, buffer, false);
  36. ucl0Log.info(buffer.toString());
  37.  
  38. File jar1 = new File(chapDir+"/j1.jar");
  39. Logger ucl1Log = Logger.getLogger("UCL1");
  40. ucl1Log.info("jar1 path: "+jar1.toString());
  41. URL[] cp1 = {jar1.toURL()};
  42. Ex0URLClassLoader ucl1 = new Ex0URLClassLoader(cp1);
  43. Class obj2Class2 = ucl1.loadClass("org.jboss.chap2.ex0.ExObj2");
  44. buffer.setLength(0);
  45. buffer.append("ExObj2 Info, UCL1");
  46. Debug.displayClassInfo(obj2Class2, buffer, false);
  47. ucl1Log.info(buffer.toString());
  48.  
  49. ucl0.setDelegate(ucl1);
  50. try
  51. {
  52. ucl0Log.info("Try ExCtx.newInstance()");
  53. Object ctx0 = ctxClass1.newInstance();
  54. ucl0Log.info("ExCtx.ctor succeeded, ctx0: "+ctx0);
  55. }
  56. catch(Throwable e)
  57. {
  58. ucl0Log.error("ExCtx.ctor failed", e);
  59. }
  60. }
  61. }

----------------------------------------------------------------------------

  1. package org.jboss.chap2.ex0;
  2.  
  3. import java.net.URLClassLoader;
  4. import java.net.URL;
  5.  
  6. import org.apache.log4j.Logger;
  7.  
  8. /** A custom class loader that overrides the standard parent delegation model
  9. * @author Scott.Stark@jboss.org
  10. * @version $Revision:$
  11. */
  12. public class Ex0URLClassLoader extends URLClassLoader
  13. {
  14.  
  15. private static Logger log = Logger.getLogger(Ex0URLClassLoader.class);
  16. private Ex0URLClassLoader delegate;
  17.  
  18. public Ex0URLClassLoader(URL[] urls)
  19. {
  20. super(urls);
  21. }
  22.  
  23. void setDelegate(Ex0URLClassLoader delegate)
  24. {
  25. this.delegate = delegate;
  26. }
  27.  
  28. protected synchronized Class loadClass(String name, boolean resolve)
  29. throws ClassNotFoundException
  30. {
  31. Class clazz = null;
  32. if( delegate != null )
  33. {
  34. log.debug(Integer.toHexString(hashCode())+"; Asking delegate to loadClass: "+name);
  35. clazz = delegate.loadClass(name, resolve);
  36. log.debug(Integer.toHexString(hashCode())+"; Delegate returned: "+clazz);
  37. }
  38. else
  39. {
  40. log.debug(Integer.toHexString(hashCode())+"; Asking super to loadClass: "+name);
  41. clazz = super.loadClass(name, resolve);
  42. log.debug(Integer.toHexString(hashCode())+"; Super returned: "+clazz);
  43. }
  44. return clazz;
  45. }
  46.  
  47. protected Class findClass(String name)
  48. throws ClassNotFoundException
  49. {
  50. Class clazz = null;
  51. log.debug(Integer.toHexString(hashCode())+"; Asking super to findClass: "+name);
  52. clazz = super.findClass(name);
  53. log.debug(Integer.toHexString(hashCode())+"; Super returned: "+clazz);
  54. return clazz;
  55. }
  56. }

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:

  1. public ExCtx() throws IOException
  2. {
  3. value = new ExObj();
  4. Logger log = Logger.getLogger(ExCtx.class);
  5. StringBuffer buffer = new StringBuffer("ctor.ExObj");
  6. Debug.displayClassInfo(value.getClass(), buffer, false);
  7. log.info(buffer.toString());
  8. ExObj2 obj2 = value.ivar;
  9. buffer.setLength(0);
  10. buffer = new StringBuffer("ctor.ExObj.ivar");
  11. Debug.displayClassInfo(obj2.getClass(), buffer, false);
  12. log.info(buffer.toString());
  13. }

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

Buildfile: build.xml

...

[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
 

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.

Obtaining debugging information for a Class

Class clazz = ...;

StringBuffer results = new StringBuffer();

ClassLoader cl = clazz.getClassLoader();

results.append("\n"+clazz.getName()+"("+Integer.toHexString(clazz.hashCode())+").ClassLoader="+cl);

ClassLoader parent = cl;

while( parent != null )

{

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 )

break;

if( parent != null )

parent = parent.getParent();

}

CodeSource clazzCS = clazz.getProtectionDomain().getCodeSource();

if( clazzCS != null )

results.append("\n++++CodeSource: "+clazzCS);

else

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..

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"/>

</layout>

</appender>

<category name="org.jboss.mx.loading" additivity="false">

<priority value="TRACE" class="org.jboss.logging.XLevel"/>

<appender-ref ref="UCL"/>

</category>

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 .

Inside the JBoss Class Loading Architecture

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.

 

FIGURE 2-3. The JBoss 3.x core class loading components

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:

  1. Check the UnifiedLoaderRepository3 classes cache associated with the UnfiedClassLoader3 . If the class is found in the cache it is returned.
  2. Else, ask the UnfiedClassLoader3 if it can load the class. This is essentially a call to the superclass URLClassLoader.loadClass(String, boolean) method to see if the class is among the URLs associated with the class loader, or visible to the parent class loader. If the class is found it is placed into the repository classes cache and returned.
  3. Else, the repository is queried for all UCLs that are capable of providing the class based on the repository package name to UCL map. When a UCL is added to a repository an association between the package names available in the URLs associated with the UCL is made, and a mapping from package names to the UCLs with classes in the package is updated. This allows for a quick determination of which UCLs are capable of loading the class. The UCLs are then queried for the requested class in the order in which the UCLs were added to the repository. If a UCL is found that can load the class it is returned, else a java.lang.ClassNotFoundException is thrown.
Viewing Classes in the Loader Repository

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.

 

FIGURE 2-4. The default class LoaderRepository MBean view in the JMX console

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

Repository cache version:

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 )

Implemented Interfaces:

++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.

Scoping Classes

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..

An example jboss-app.xml descriptor for enabled scoped class loading at the ear level.

<jboss-app>

<loader-repository>some.dot.com:loader=webtest.ear</loader-repository>

</jboss-app>

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 Complete Class Loading Model

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.

 

FIGURE 2-5. A complete class loader view

The following points apply to See A complete class loader view.:

  • System ClassLoaders. The System ClassLoaders node refers to either the thread context class loader (TCL) of the VM main thread or of the thread of the application that is loading the JBoss server if it is embedded.
  • ServerLoader. The ServerLoader node refers to the a URLClassLoader that delegates to the System ClassLoaders and contains the following boot URLs
  • All URLs referenced via the jboss.boot.library.list system property. These are path specifications relative to the libraryURL defined by the jboss.lib.url property. If there is no jboss.lib.url property specified, it default to the jboss.home.url + /lib/. If there is no jboss.boot.library property specified, it defaults to jaxp.jar, log4j-boot.jar, jboss-common.jar, and jboss-system.jar.
  • The JAXP jar which is either crimson.jar or xerces.jar depending on the -j option to the Main entry point. The default is crimson.jar.
  • The JBoss JMX jar and GNU regex jar, jboss-jmx.jar and gnu-regexp.jar.
  • Oswego concurrency classes jar, concurrent.jar
  • Any jars specified as libraries via -L command line options
  • Any other jars or directories specified via -C command line options
  • Server. The Server node represent a collection of UnifiedClassLoader3 s created by the org.jboss.system.server.Server interface implementation. The default implementation creates UCLs for the patchDir entries as well as the server conf directory. The last UCL created is set as the JBoss main thread context class loader. This will be combined into a single UCL now that multiple URLs per UCL are supported.
  • All UnifiedClassLoader3 s. The All UnifiedClassLoader3 s node represents the UCLs created by deployers. This covers EARs, jars, WARs, SARs and directories seen by the deployment scanner as well as jars referenced by their manifests and any nested deployment units they may contain. This is a flat namespace and there should not be multiple instances of a class in different deployment jars. If there are, only the first loaded will be used and the results may not be as expected. There is a mechanism for scoping visibility based on EAR deployment units that we discussed in See Scoping Classes.. Use this mechanism if you need to deploy multiple versions of a class in a given JBoss server.
  • EJB DynClassLoader. The EJB DynClassLoader node is a subclass of URLClassLoader that is used to provide RMI dynamic class loading via the simple HTTP WebService. It specifies an empty URL[] and delegates to the TCL as its parent class loader. If the WebService is configured to allow system level classes to be loaded, all classes in the UnifiedLoaderRepository3 as well as the system classpath are available via HTTP.
  • EJB ENCLoader. The EJB ENCLoader node is a URLClassLoader that exists only to provide a unique context for an EJB deployment's java:comp JNDI context. It specifies an empty URL[] and delegates to the EJB DynClassLoader as its parent class loader.
  • Web ENCLoader. The Web ENCLoader node is a URLClassLoader that exists only to provide a unique context for a web deployment's java:comp JNDI context. It specifies an empty URL[] and delegates to the TCL as its parent class loader.
  • WAR Loader. The WAR Loader is a servlet container specific ClassLoader that delegates to the Web ENCLoader as its parent class loader. The default behavior is to load from its parent class loader and then the war WEB-INF/{classes,lib} directories. If the servlet 2.3 class loading model is enabled it will first load from the war WEB-INF/{classes,lib} and then the parent class loader.

In its current form there are some advantages and disadvantages to the 3.x class loading architecture. Advantages include:

  • Classes do not need to be replicated across deployment units in order to have access to them.
  • Many future possibilities including novel partitioning of the repositories into domains, dependency and conflict detection, etc.

Disadvantages include:

  • Existing deployments may need to be repackaged to avoid duplicate classes. Duplication of classes in a loader repository can lead to ClassCastException s and LinkageError s depending on how the classes are loaded. As of the 3.0.5RC1 release this should generally not be a problem, but may still exist.
  • Deployments that depend on different versions of a given class need to be isolated in separate ears and a unique HeirarchicalLoaderRepository3 defined using a jboss-app.xml descriptor.

JBoss XMBeans

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..

 

FIGURE 2-6. The JBoss 1.0 XMBean DTD Overview (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.

Descriptors

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.

FIGURE 2-7. The descriptors element content model

The key descriptors child elements include:

  • interceptors: The interceptors element specifies a customized stack of interceptors that will be used in place of the default stack. Currently this is only used when specified at the mbean level, but it could define a custom attribute or operation level interceptor stack in the future. The content of the interceptors element specifies a custom interceptor stack. If no interceptors element is specified the standard ModelMBean interceptors will be used. The standard interceptors are:
  • org.jboss.mx.interceptor.PersistenceInterceptor
  • org.jboss.mx.interceptor.MBeanAttributeInterceptor
  • org.jboss.mx.interceptor.ObjectReferenceInterceptor
  • 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 .

  • persistence: The persistence element allows the specification of the persistPolicy, persistPeriod, persistLocation, and persistName persistence attributes suggested by the JMX specification. The persistence element attributes are:
  • persistPolicy: The persistPolicy attribute defines when attributes should be persisted and its value must be one of:
  • Never, attribute values are transient values that are never persisted
  • OnUpdate, attribute values are persisted whenever they are updated
  • OnTimer, attribute values are persisted based on the time given by the persistPeriod.
  • NoMoreOftenThan, attribute values are persisted when updated unless but no more oten than the persistPeriod.
  • persistPeriod: The persistPeriod attribute gives the update frequency in milliseconds if the perisitPolicy attribute is NoMoreOftenThan or OnTimer.
  • persistLocation: The persistLocation attribute specifies the location of the persistence store. Its form depends on the JMX peristence implementation. Currently this should refer to a directory into which the attributes will be serialized if using the default JBoss persistence manager.
  • persistName: The persistName attribute can be used in conjunction with the persistLocation attribute to further qualify the persistent store location. For a directory persistLocation the persistName specifies the file to which the attributes are stored within the directory.
  • currencyTimeLimit: The currencyTimeLimit element specifies the time in seconds that a cached value of an attribute remains valid. Its value attribute gives the time in seconds. A 0 value indicates that an attribute value should always be retrieved from the mbean and never cached. A -1 value indicates that a cache value is always valid.
  • state-action-on-update: The state-action-on-update element specifies the what happens to an mbean when one of its attributes is updated. The action is given by the value attribute. Its value attribute defines what happens to the mbean lifecycle state when one of its attributes is update. It must be one of:
  • keep-running,
  • restart,
  • reconfigure,
  • reinstantiate
  • However, note that this descriptor is not currently used.

  • display-name: The display-name element specifies the human friendly name of an item.
  • default: The default element specifes a default value to use when a field has not been set. Note that this value is not written to the MBean on startup as is the case with the jboss-service.xml attribute element content value. Rather, the default value is used only if there is no attribute accessor defined, and there is no value element defined.
  • value: The value element specifies a management attribute's current value. Unlike the default element, the value element is written through to the MBean on startup provided there is a setter method available.
  • persistence-manager: The persistence-manager element give the name of a class to use as the persistence manager. The value attribute specifies the class name that supplies the org.jboss.mx.persistence.PersistenceManager interface implementation. The only implementation currently supplied by JBoss is the org.jboss.mx.persistence.ObjectStreamPersistenceManager which serializes the ModelMBeanInfo content to a file using Java serialization.
  • descriptor: The descriptor element specifies an arbitrary descriptor not know to JBoss. Its name attribute specifies the type of the descriptor and its value attribute specifies the descriptor value. The descriptor element allows for the attachment of arbitrary management metadata.

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 Management Class

The class element specifies the fully qualified name of the managed object whose management interface is described by the XMBean descriptor.

The Constructors

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..

 

FIGURE 2-8. The XMBean constructor element and its content model

The key child elements are:

  • description: A description of the constructor.
  • name: The name of the constructor, which must be the same as the implementation class.
  • parameter: The parameter element describes a constructor parameter. The parameter element has the following attributes:
  • description: An optional description of the parameter.
  • name: The required variable name of the parameter.
  • type: The required fully qualified class name of the parameter type.
  • descriptors: Any descriptors to associate with the constructor metadata.

The Attributes

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..

 

FIGURE 2-9. The XMBean attribute element and its content model

The attribute element supported attributes include:

  • access: The optional access attribute defines the read/write access modes of an attribute. It must be one of:
  • read-only: The attribute may only be read.
  • write-only: The attribute may only be written.
  • read-write: The at