Know your Oracle application server

Troubleshooting OC4J and WebLogic for Java EE application deployment

Subtle incompatibilities between your application server and certain Java EE application components or third-party libraries can sink your ship at deployment time. In this article, René van Wijk presents tips for configuring Oracle WebLogic and OC4J for Java EE applications. Find out where the trouble spots are and how to fix them when deploying Enterprise JavaBeans, TopLink Essentials, Hibernate with ANTLR, and Apache MyFaces Trinidad components in your WebLogic or OC4J app server. Level: Beginner

A Java application server is the runtime environment for a Java enterprise application. It provides services to components such as servlets and Enterprise JavaBeans (EJBs) in the form of containers. A container is an interface between a component and the platform-specific functionality that supports it.

An enterprise bean must be deployed to an EJB container, which then manages the bean's execution; a servlet must be deployed to a Web container, which manages its execution. The deployment process requires you to provide settings for the components that can alter the application server's underlying services, such as security and transaction management. For example, you can configure a component's security so that only authorized users can access certain resources. Or you can specify relationships among methods so that they are treated as a unit in a single transaction.

Because application servers offer configurable services, components can behave differently depending on where you deploy them. Containers also manage nonconfigurable services, such as lifecycle management and data persistence. EJBs, as well as external frameworks and libraries that you use in your application, can give you the means to configure these services. But the process isn't always straightforward. Each application server has its idiosyncrasies, which can create challenges for the new user. The application server's own system classes, for example, might interfere with classes belonging to the framework you use. In this article I offer tips for resolving these kinds of issues when deploying Java EE applications to two popular application servers: Oracle Application Server Containers for J2EE (OC4J) and WebLogic Server.

I'll start by explaining what's required in order to successfully deploy an application that uses the TopLink Essentials Java persistence implementation on a WebLogic server. Next, I'll introduce some tweaks required for using EJBs on both WebLogic and OC4J. A JavaServer Faces (JSF) component library called Apache MyFaces Trinidad is the next stop. Trinidad uses a partial page rendering mechanism, which doesn't function properly on WebLogic servers without the solution I'll introduce. Finally, I'll explore class-loading mechanisms, focusing on the ANTLR library through an example that deploys an application that uses Hibernate.

Java persistence with TopLink Essentials

The Java Persistence API is a standard for managing relational data in Java applications. It uses object/relational mapping (ORM) to bridge the gap between an object-oriented model and a relational database. WebLogic server's built-in persistence framework, OpenJPA, works out of the box. But if you'd prefer to use the TopLink Essentials framework instead of OpenJPA, your application won't deploy on a WebLogic server without some changes. If you try to proceed without proper configuration, the following exception will occur:

java.lang.IllegalArgumentException: URI is not hierarchical
at java.io.File.<init>(File.java:335)
at oracle.toplink.essentials.ejb.cmp3.persistence.ArchiveFactoryImpl.createArchive
(ArchiveFactoryImpl.java:104)

This exception can be traced to the fact that the WebLogic server sends incorrect URLs to TopLink Essentials. Specifically, the URLs have the form of jar:file:c/:something.jar. According to the URI specification, this is an invalid URI because no / precedes the schema (jar:file:) definition. In all cases the URI is determined to be opaque and is thus unacceptable for the java.io.File(URI) constructor.

The solution is to change the following line of code in [WebLogic's] createArchive(URL):

File f = new File(uri);

to:

File f;
if (!uri.isOpaque()) {
    f = new File(uri);
} else {
    f = new File(url.getPath()):
}

Enabling DML operations

To perform Data Manipulation Language (DML) operations using TopLink Essentials on a WebLogic server, you also need a transaction controller, such as the one shown in Listing 1:

Listing 1. WebLogic transaction controller

import javax.transaction.TransactionManager;
import oracle.toplink.essentials.transaction.JTATransactionController;

public class WebLogicTransactionController extends JTATransactionController {

    private static final String JNDI_TRANSACTION_MANAGER_NAME =
                                    "javax.transaction.TransactionManager";

    public WebLogicTransactionController() {
    }

    protected TransactionManager acquireTransactionManager() throws Exception {
        return (TransactionManager)jndiLookup(JNDI_TRANSACTION_MANAGER_NAME);
    }
}


You must register the WebLogicTransactionController class in the persistence.xml file, as shown in Listing 2:

Listing 2. Registering WebLogicTransactionController in persistence.xml

<?xml version="1.0" encoding="windows-1252" ?>
<persistence ...>
    <persistence-unit name="JavaWorld">
        <provider>oracle.toplink.essentials.PersistenceProvider</provider>
        ...
        <properties>
            <property name="toplink.logging.level" value="FINE"/>
            <property name="toplink.target-database" value="Oracle"/>
            <property name="toplink.cache.shared.default" value="false"/>
            <property name="toplink.target-server"
                      value="model.utils.WebLogicTransactionController"/>
        </properties>
    </persistence-unit>
</persistence>

The code that Listing 2 adds to the persistence.xml file defines a persistence unit named JavaWorld. The file also defines a provider -- in this case TopLink Essentials -- and some properties to configure the provider. The toplink.target-server property tells the provider it will be deployed on a certain server, in this case WebLogic server.

Controlling cache access

Every application using a persistent state must interact with the persistence provider when the state held in memory must be propagated to the database (or vice versa). In other words, the persistence provider's interface must be used in order to store and load objects. This interface is the EntityManager. Each EntityManager instance is associated with a persistence context. A persistence context can be viewed as a sort of cache that keeps track of which objects were changed during a certain transaction. The persistence context is invisible to the application; that is, it's not an API that can be called.

By default, TopLink Essentials uses a cache that is shared by clients bound to a certain session. For example, if a client retrieves an object from the database or stores an object in the database, a copy of the object is stored in the application server's cache. Other clients can subsequently retrieve this object from the cache. The following problem now arises: Say you add an entity, then remove it, and then add it again to the database. The last action causes an exception, which states that the entity already exists. To resolve this issue, the toplink.cache.shared.default property must be set to false, as shown in Listing 2. This property lets you control if the cache can be shared by multiple clients or not. If you set it to false, the cache is exclusively used by one client. This client can still refer to objects in a shared cache, but other clients are not allowed to refer to objects in the exclusive cache.

Loading TopLink classes at startup

As a final step, the classes used by TopLink Essentials must be added to the WebLogic server's classpath. A WebLogic server operates with domains -- the basic administration units for WebLogic server instances. Each domain has its own configuration files, including a file to configure which classes are loaded during startup. This file, which is in the domain-home/bin directory, is called setDomainEnv. To load the TopLink Essentials classes during startup, you must add this section to the setDomainEnv file:

@REM SET THE CLASSPATH
set TOPLINK=..\..\..\wlserver_10.3\ADF\lib\toplink-essentials.jar
set TOPLINK=%TOPLINK%;..\..\..\wlserver_10.3\ADF\lib\toplink-essentials-agent.jar
set CLASSPATH=%TOPLINK%;%PRE_CLASSPATH%...

The alterations I've just described are unnecessary if you use OC4J, because TopLink Essentials is OC4J's built-in persistence framework.

Enterprise beans

EJBs are components that encapsulate an application's business logic. Java EE's Java Naming and Directory Interface (JNDI) allows you to use an enterprise bean in a client, or enable the bean to use some resource configured on an application server. JNDI lets applications associate attributes with objects and search for those objects by using their attributes. For example, an application using JNDI can store and retrieve any type of Java object by its given JNDI name.

Consider an enterprise bean registered under the name ejb/JavaWorld:

@Remote
public interface JavaWorld {...}

@Stateless(name = "ejb/JavaWorld", mappedName = "ejb/JavaWorld")
public class JavaWorldBean implements JavaWorld {...}

This enterprise bean can subsequently be referred to by a client using a JNDI lookup. OC4J requires that the name associated with the name attribute be used. WebLogic requires that the name associated with the mappedName attribute be used.

A container implements the environment for a component and presents this as a JNDI context. A component can approach this environment by using the JNDI interfaces: a component creates an InitialContext object to retrieve the context. This context can then be used to look up registered objects such as the ejb/JavaWorld enterprise bean. If you're using OC4J, you can retrieve the enterprise bean with:

Context context = new InitialContext();
JavaWorld javaWorld = (JavaWorld)context.lookup("ejb/JavaWorld");

For WebLogic server, you must use:

Context context = new InitialContext();
JavaWorld javaWorld =
    (JavaWorld)context.lookup("ejb/JavaWorld#datamodel.logic.JavaWorld");

In the last line of this code, the value of the mappedName attribute precedes the #, and the package plus the Java class name of the business interface follows the #. Note that the mappedName attribute is used to refer to resources configured in the entire application server, not just the application. OC4J doesn't support this attribute.

Configuring resources

In general, it's preferable to configure resources in an application server instead of doing all the coding yourself. A Java Message Service (JMS) service is one such resource. JMS is a messaging standard that lets EJB components create, send, receive, and read messages. To configure a JMS provider on a WebLogic server, you must follow a few steps:

  1. Create a persistence store -- a physical repository for storing data related to the system.
  2. Create a JMS server -- a container that manages queues and topics.
  3. Configure JMS resources, such as queues and topics, as JMS modules.
  4. Create a sub-deployment -- a mechanism that groups JMS resources and binds them to the JMS server.
  5. Create JMS resources -- queues, topics, and connection factories. Queues and topics are destinations that a client can specify as the target or the source, respectively, of the produced or consumed messages. A connection factory is used to connect to a JMS server.

Next, add the persistent store, JMS server, and the JMS modules to the config.xml file in the domain-home/config directory. Put the JMS resources into a separate file located in the domain-home/config/jms directory. This file contains the provided JNDI names. If you use OC4J, you must add the JMS resources to the jms.xml file located in the oc4j-home/config directory.

Resource injection

Java EE lets you retrieve configured resources by using resource injection. Suppose you have configured a connection factory and registered it using the JNDI name jms/JavaWorld. With OC4J, you can retrieve this resource by using:

@Resource(name = "jms/JavaWorld")
private ConnectionFactory javaWorld;

On a WebLogic server, resource injection is not supported by itself. If a certain enterprise bean wants to use configured resources, you must add these resources to the weblogic-ejb-jar.xml file. This file, which defines the beans, can be used to override certain deployment settings. Of course you can still use a JNDI lookup to retrieve the resource.

If an application is being deployed, you can also create a deployment plan that contains various module-override sections. This example deployment-plan section specifies a number of files in which you can define the overrides:

<weblogic-ejb-jar...>
  <weblogic-enterprise-bean>
    <ejb-name>JavaWorld</ejb-name>
    <enable-call-by-reference>True</enable-call-by-reference>
    <jndi-name>ejb/JavaWorld</jndi-name>
  </weblogic-enterprise-bean>
</weblogic-ejb-jar>

Note that the resources to be used by the enterprise bean can be defined in the deployment plan.

1 2 Page 1
Page 1 of 2