Packaging EJB 3 Applications, Page 2
Exploring class loading
There is a misconception among many developers that all classes are loaded into memory when the JVM starts up; this is not true. Classes are loaded dynamically as and when they are needed at runtime. This process of locating the byte code for a given class name and converting that code into a Java class instance is known as class loading. Your application may have hundreds of EJBs and other resources; loading all these classes into the JVM consumes a lot of memory. Most application servers use a sophisticated mechanism to load classes as and when needed. Therefore, your EJB class will be loaded into memory only when a client accesses it. However, it is implementation specific. Application servers support the bean pooling mechanism, so EJB classes would be loaded into memory while some instances would be instantiated and put into the pool during deployment time.
When you build an application using EJB 3, you may use third-party libraries such as Log4J or you may depend on an in-house shared library configured in the application server. You may have web applications that depend on your EJB components and entities. As you can see, a complex application may depend on libraries available at several places. This means that you may run into many deployment errors such as ClassNotFoundException or ClassNoDefException. Understanding the class-loading concepts will educate you on effectively packaging your EJB 3 applications and help you troubleshoot any deployment-related issues.
In this section, we introduce the concept of class loading and look at the class-loader hierarchy in an application server. We then expose the parent delegation model. Finally, we examine class loading in Java EE and explore the dependencies between different modules.
If you've built simple applications with Java, you must be aware that when you run your application, the classes that make it up (often packaged in a standard JAR file) are made available to the JVM through the CLASSPATH environment variable. When a particular class is invoked, the JVM loads that class into memory by locating it from the available byte code files provided either via JAR files in the CLASSATH or a specified directory structure.
Class loading is initially performed by the JVM when it starts up. It loads the essential classes required, and then subclasses of the java.lang.ClassLoader class take the lead. These class loaders allow applications to load classes dynamically that may not be required during the compilation process. By default, the JVM utilizes a few different class loaders. As an illustration, the Sun JVM has a hierarchy of three loaders, as shown in figure 2.
The boot class loader loads all platform classes that the Java language requires, such as classes in the java.lang or java.util package. You can optionally use the bootclasspath command-line option of the JVM to instruct the boot class loader to load additional classes from other JAR files.
The extension class loader is a child class loader of the boot class loader, and loads classes from any JARs placed in the $JAVA_HOME/jre/lib/ ext directory, or in a separate directory specified with the .Dj ava.ext.dir system property. By default, it loads the Java cryptography library, as well as the security classes.
The system class loader actually loads application classes as specified by an application, and is also known as the application class loader. You can use several mechanisms to specify the location from which the system class loader loads classes. One way is to specify the CLASSPATH environment variable. Another is to specify the manifest Class-Path entry of a JAR file that is being executed or that is in the CLASSPATH.
For example, the JAR file actionBazaar-client.jar has a Manifest.mf file in the META-INF directory that has this entry:
When the class loader loads the classes, it will search not only for the required class in the actionBazaar-client.jar, but also in the actionBazaar-utility.jar. The location of the JAR specified in the manifest Class-Path is relative to the JAR file that contains it.
For a simple Java application, this process is probably as simple as packaging the classes in a JAR and making the file available in the CLASSPATH. However, in a sophisticated environment such as Java EE, the application servers utilize several mechanisms to load the classes from a variety of locations, such as an application module, a library module, or a shared library configured in the application server environment.
When you start up an application server, a Java process starts loading classes required for the application server. When you deploy and execute an application in a Java application server, the application server loads the classes dynamically by creating new instances of class loaders.
Exposing the classic parent delegation model
You must be curious as to why JVM always loads the class from the parent class loader. In this section we will uncover the reason.
Let's review the scenario for ActionBazaar in order to understand the class-loading delegation model. The ActionBazaar website is built with JSP pages that invoke EJBs. When a user visits the ActionBazaar website and browses the items listed for auction, the application server uses class loaders to dynamically load required classes from application modules. All class loaders follow a standard algorithm to load classes, as illustrated in figure 3.
A class loader loads a class dynamically on an as-needed basis. It first looks at its local cache to see if it was loaded earlier. If not, it asks its parent to load the class. If its parent cannot load the class, it attempts to load it from its local code sources. Simply put, a code source is a base location, such as a JAR file, which the JVM searches for classes. This approach is called the Parent First delegation model.
Now that we've reviewed the basics of Java class loading, let's quickly review how class loading works in a Java EE application.
Class loading in Java EE applications
As we discussed earlier, an EJB application may make use of third-party libraries. In order to enable that, most Java EE containers use sophisticated mechanisms to load classes from a variety of places. You may remember from previous discussions that we follow standard practices to package our application components into standard-compliant archives such as EAR, EJB-JAR, WAR, and so forth. Table 2 lists the code sources for commonly used Java EE modules. For simplicity we are ignoring resource adapter (RAR) modules.
Table 2 A standard archive may load classes either packaged inside it or from any other archives it is dependent on.
The sooner you develop a good understanding of how the packaging standards work, the easier the whole packaging and deployment process will be.
Dependencies between Java EE modules
Unfortunately, no Java EE specification provides a standard for class loading, and each application server implements class loaders in whatever way seems best to the vendor. However, Java EE defines the visibility and sharing of classes between different modules, and we can depict the dependency between different modules as shown in figure 4.
As illustrated in figure 4, the EAR class loader loads all JARs in the lib directory that is shared between multiple modules. Typically a single EJB class loader loads all EJB packaged in all EJB-JAR modules. The EJB class loader is often the child of the application class loader, and loads all EJB classes. Because the EJB is a child to the EAR class loader, all classes loaded at the> EAR level will be visible to the EJBs.
Figure 4: Illustration of class visibility of an EAR file containing multiple web modules, EJBs, and shared library modules. The EAR class loader loads the classes in the JARs packaged as library modules, and all classes loaded by the EAR class loader are visible to the EJBs. The classes loaded by EJB class loader are typically visible to the web module in most containers because the WAR class loader is a child of the EJB class loader.
EJBs are accessible from WAR modules. Furthermore, the EJB class loader is the parent of the WAR application class loader, and all EJB classes will be visible to the WAR module by default.
So before we move on to packaging EJBs, let's recap how this is going to help in packaging EJB 3 applications. If you package classes in a specific EJB module, it will probably be visible to only that module. If you want your classes (helper and utility) to be visible to all modules in the EAR file, you can package them as a library module in the EAR.
Armed with this knowledge on class loading, we can now return to the discussion on packaging EJBs. First we'll talk about the packaging of session and message-driven beans, and quickly proceed to the packaging of persistence entities.