Skip Headers
Oracle® Containers for J2EE Enterprise JavaBeans Developer's Guide
10g Release 3 (10.1.3)
B14428-02
  Go To Documentation Library
Home
Go To Product List
Solution Area
Go To Table Of Contents
Contents
Go To Index
Index

Previous
Previous
 
Next
Next
 

29 Accessing an EJB from a Client

This chapter explains how to access an EJB from a client, including:

For more information, see:


Note:

You can download EJB code examples from: http://www.oracle.com/technology/tech/java/oc4j/demos.

What Type of Client Do You Have?

You can access an EJB from a variety of clients, including:

How you access an EJB, resource, or environment variable is different depending on the type of client and how the application is assembled and deployed.

For more information, see "Configuring the Client".

EJB Client

When one EJB (call it the source EJB) accesses another EJB (call it the target EJB), the source EJB is the client of the target EJB.

If you are using EJB 3.0, using annotations and dependency injection, OC4J initializes the instance variable that corresponds to the target reference.

If you are using EJB 2.1, you must use JNDI lookup in this scenario.

Standalone Java Client

A standalone Java client is a client that executes outside of OC4J but accesses EJB resources deployed to OC4J.

Typically, a standalone Java client accesses EJB resources by making use of Java RMI calls. You must code a standalone Java client so that it honors the security and authentication requirements that OC4J enforces.

By default, OC4J is configured to assign RMI ports dynamically within a set range. In this release, you can look up an OC4J-deployed EJB from a standalone Java client without specifying an exact RMI port. You do not need to configure OC4J to use exact port numbers.

If you are using EJB 3.0, note that annotations and dependency injection is not supported for a standalone Java client.

If you are using EJB 2.1, you must configure your initial context to accommodate this scenario (see "Accessing an EJB 2.1 EJB Using RMI from a Standalone Java Client").

Servlet or JSP Client

A servlet or JSP can access an EJB.

OC4J does not support dependency injection in the Web container. Consequently, you can only use JNDI lookup from a servlet or JSP client for both EJB 3.0 and EJB 2.1 applications.

Configuring the Client

Before you can access an EJB from a client, you must consider the following:

Configuring the Client Classpath for OC4J

Table 29-1 lists the OC4J-specific JAR files that you must include on your client's classpath. The Source column indicates from where you get a copy of the required JAR.

Table 29-1 OC4J Client Classpath Requirements

OC4J JAR Source All Clients OracleAS JMS Client Oracle AQ JMS Client J2CA JMS Client

oc4jclient.jar

<OC4J_HOME>/j2ee/<instance>

Supported
Supported
Supported
Supported

ejb.jar

<OC4J_HOME>/j2ee/<instance>/lib

Supported
Supported
Supported
Supported

jndi.jar

<OC4J_HOME>/j2ee/<instance>/lib

Supported
Supported
Supported
Supported

optic.jarFoot 1 

<OC4J_HOME>/opmn/lib

Supported
Supported
Supported
Supported

jta.jar

<OC4J_HOME>/j2ee/<instance>/lib

Not Supported
Supported
Supported
Supported

jms.jar

<OC4J_HOME>/j2ee/<instance>/lib

Not Supported
Supported
Supported
Supported

javax77.jar

<OC4J_HOME>/j2ee/<instance>/lib

Not Supported
Supported
Supported
Supported

adminclient.jar

<OC4J_HOME>/j2ee/<instance>/lib

Not Supported
Not Supported
Supported
Not Supported

jdbc.jar

<OC4J_HOME>/j2ee/<instance>/../../lib

Not Supported
Not Supported
Supported
Not Supported

dms.jar

<OC4J_HOME>/j2ee/<instance>/lib

Not Supported
Not Supported
Supported
Not Supported

J2CA connector JAR

Connector provider

Not Supported
Not Supported
Not Supported
Supported

Footnote 1 Required only if you plan to use the opmn:ormi prefix in JNDI look up with Context.PROVIDER_URL (see "Configuring an Oracle Initial Context Factory").

If you download any of these JAR files into a browser, you must grant certain permissions (see "Granting Permissions in Browser").

Selecting an Initial Context Factory Class

You use an initial context factory to obtain an initial context: a reference to the JNDI namespace. Using the initial context, you can use the JNDI API to look up an EJB, resource manager connection factory, environment variable, or other JNDI-accessible object. The type of initial context factory you use depends on your client type and how you are using OC4J: standalone or as part of Oracle Application Server (see "Configuring the Initial Context Factory").

Specifying Security Credentials

If the client and target EJB are not collocated, not deployed in the same application, and the target EJB application is not the client's parent, then your client must specify its credentials before accessing the target EJB ( see "Specifying Credentials in EJB Clients").

Selecting an EJB Reference

In EJB 3.0, to access an EJB 3.0 EJB or resource in an EJB client, you can use annotations, resource injection, and default JNDI names (based on class and interface names) instead of doing a JNDI look up with a predefined environment references.

In EJB 2.1 or in EJB 3.0 (for standalone Java clients or servlet or JSP clients), to access an EJB or resource, you must do a JNDI look up on a predefined environment references (see "Configuring Environment References"). To access an EJB 2.1 EJB or resource, choose the appropriate predefined environment reference (actual or logical; local or remote) and look it up using a JNDI initial context (see "Selecting an Initial Context Factory Class").

If you access an EJB by reference from within your client implementation, perform a JNDI lookup using the <ejb-ref-name> defined in the EJB deployment descriptor. For more information on defining an EJB reference to a target EJB, see "Configuring an Environment Reference to an EJB".

Table 29-2 shows when to prefix the reference with java:comp/env/ejb/, which is where the container places the EJB references defined in the deployment descriptor.

Table 29-2 When to Use the java:comp/env/ejb/ Prefix

Client Initial Context Factory Use Prefix?

EJB Client


Default

RMIInitialContext

Optional

Not Used

Standalone Java Client


Default

ApplicationClientInitialContext

Optional

Mandatory

Servlet or JSP Client


Default

RMIInitialContext

Optional

Not Used


Example 29-1 shows how to look up an EJB with logical name ejb/HelloWorld using the java:comp/env/ejb/ prefix and Example 29-2 shows how to look up this EJB without the prefix.

Example 29-1 Looking Up an EJB with the Prefix

InitialContext ic = new InitialContext();
HelloHome hh = (HelloHome)ic.lookup("java:comp/env/ejb/HelloWorld");

Example 29-2 Looking Up an EJB without the Prefix

InitialContext ic = new InitialContext();
HelloHome hh = (HelloHome)ic.lookup("ejb/HelloWorld");

Accessing an EJB 3.0 EJB

You can directly lookup a bean instance from JNDI (or use resource injection in an EJB 3.0 EJB client) and retrieve a bean instance without the home interface. If the <home> or <local-home> element is removed from an EJB reference, a bean instance is returned from JNDI instead of the home.

The bean instance is created by executing the no-argument create method on the home interface. Stateful session beans and entity beans can also use this shortcut, but they must have a no-argument create method or an exception will be thrown at lookup time.

In both cases, the syntax used in obtaining the reference to the EJB business interface is independent of whether the business interface is local or remote. In the case of remote access, the actual location of a referenced enterprise bean and EJB container are, in general, transparent to the client using the remote business interface of the bean.

For more information, see "Looking up an EJB 3.0 EJB".

Accessing an EJB 3.0 EJB in Another Application

Normally, you cannot have EJBs communicating across EAR files, that is, across applications that are deployed in separate EAR files. The only way for an EJB to access an EJB that was deployed in a separate EAR file is to declare it to be the parent of the client. Only children can invoke methods in a parent.

For example, there are two EJBs, each deployed within their EAR file, called sales and inventory, where the sales EJB needs to invoke the inventory EJB to check to see if enough widgets are available. Unless the sales EJB defines the inventory EJB to be its parent, the sales EJB cannot invoke any methods in the inventory EJB, because they are both deployed in separate EAR files. So, define the inventory EJB to be the parent of the sales EJB and the sales EJB can now invoke any method in its parent.

You can only define the parent during deployment with the deployment wizard. See the "Deploying/Undeploying Applications" section in the "Using the oc4jadmin.jar Command Line Utility" chapter in the Oracle Containers for J2EE Configuration and Administration Guide on how to define the parent application of a bean.

Accessing an EJB 3.0 Entity Using an EntityManager

In an EJB 3.0 application, the javax.persistence.EntityManager is the runtime access point for persisting entities to and loading entities from the database.

This section describes:

For more information, see "How Do You Query for an EJB 3.0 Entity?".

Acquiring an EntityManager

Before you can use an EntityManager, you must acquire an EntityManager instance. How you acquire an entity manager depends on your client type ("What Type of Client Do You Have?").

When you acquire an entity manager, you specify a persistence unit. The persistence unit defines the entity manager's configuration, including details such as which factories to use, which persistent managed classes the entity manager can manage, and what object/relational mapping metadata to use. You can only acquire an entity manager for a particular persistence unit if your client is in the persistence unit's scope. For more information, see "What is the persistence.xml File?".

You can acquire an entity manager:

Acquiring the OC4J Default Entity Manager in an EJB 3.0 Stateful Session Bean Client

You can use the @PersistenceContext annotation to inject an EntityManager in an EBJ 3.0 stateful session bean client. You can use @PersistenceContext without specifying a unitName to use the OC4J default persistence unit as Example 29-3 shows.

Example 29-3 Using @PersistenceContext With the OC4J Default Persistence Unit

@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession
{
    @PersistenceContext protected EntityManager entityManager;

    public void createEmployee(String fName, String lName)
    {
        Employee employee  = new Employee();
        employee.setFirstName(fName);
        employee.setLastName(lName);
        entityManager.persist(employee);
    }
...
}

For more information, see "Understanding the OC4J Default Persistence Unit".

Acquiring a Named Entity Manager in an EJB 3.0 Stateful Session Bean Client

You can use the @PersistenceContext annotation to inject an EntityManager in an EBJ 3.0 stateful session bean client. You can use @PersistenceContext attribute unitName to specify a persistence unit by name as Example 29-4 shows. In this case, you must configure the persistence unit in a persistence.xml file.

Example 29-4 Using @PersistenceContext With a Named Persistence Unit

@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession
{
    @PersistenceContext(unitName="myPersistenceUnit") protected EntityManager entityManager;

    public void createEmployee(String fName, String lName)
    {
        Employee employee  = new Employee();
        employee.setFirstName(fName);
        employee.setLastName(lName);
        entityManager.persist(employee);
    }
...
}

For more information, see:

Acquiring an Entity Manager in Other EJB 3.0 Bean Clients

For all EJB 3.0 bean types other than stateful session beans, you can use annotations to inject a persistence context but you must use JNDI to look up the entity manager as Example 29-5 shows. In this case, you must define the persistence unit in a persistence.xml file.

Example 29-5 Using InitialContext to Lookup an EntityManager in a Stateless Session Bean

@PersistenceContext(
    name=Ópersistence/InventoryAppMgrÓ,
    unitName=InventoryManagement  // defined in a persistence.xml file
)
@Stateless
public class InventoryManagerBean implements InventoryManager
{
    EJBContext ejbContext;
    public void updateInventory(...)
    {
        ...
        // obtain the initial JNDI context
        Context initCtx = new InitialContext();
        // perform JNDI lookup to obtain container-managed entity manager
        javax.persistence.EntityManager entityManager = (javax.persistence.EntityManager) 
            initCtx.lookup("java:comp/env/persistence/InventoryAppMgr");
        ...
    }
}

For more information, see:

Acquiring an Entity Manager in a Helper Class or Web Client

To acquire an entity manager in a class that does not support annotations and injection, namely helper classes and Web clients, you must:

  1. Define a persistence unit in a persistence.xml file.

    For more information, see:

  2. Define a persistence-context-ref in the appropriate deployment descriptor file (see "Configuring an Environment Reference to a Persistence Context").

    In the persistence-context-ref, you specify:

    • persistence-context-ref: the name by which you will lookup the entity manager.

    • persistence-unit-name: the name of the persistence unit you created in step 1, that defines the characteristics of the returned entity manager.

  3. Use JNDI to look up the entity manager by the persistence-context-ref-name as Example 29-6 shows.


Note:

As Example 29-6 shows, in your Web client, you must manually demarcate a transaction using the UserTransaction API because you must use the EntityManager within a transaction.

Example 29-6 Using InitialContext to Lookup an EntityManager in a Servlet

public class InsertServlet extends HttpServlet 
{
    private static final String CONTENT_TYPE = "text/html; charset=windows-1252";
...
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws
        ServletException, IOException
    {
        ...
        UserTransaction ut = null;
        ...
        try
        {
            Context initCtx = new InitialContext();

            ut = (UserTransaction)initCtx.lookup("java:comp/UserTransaction");  
            ut.begin();  

            Employee employee = new Employee();
            employee.setEmpNo(empId);
            employee.setEname(name);
            employee.setSal(sal);

            // obtain the initial JNDI context
            Context initCtx = new InitialContext();
            // perform JNDI lookup to obtain container-managed entity manager
            javax.persistence.EntityManager entityManager = (javax.persistence.EntityManager)
                initCtx.lookup("java:comp/env/persistence/InventoryAppMgr");

            entityManager.persist(employee);

            ut.commit();

            this.getServletContext().getRequestDispatcher("/jsp/success.jsp").forward(
                request, response
            );
        }
        catch(Exception e)
        {
            ...
        }
...
    }
}

For more information, see "Configuring the Initial Context Factory".

Creating a New Entity Instance

To create a new entity instance, use EntityManager method persist passing in the entity Object as Example 29-7 shows. When you call this method, it marks the new instance for insert into the database. This method returns the same instance that you passed in.

You must call this method within a transaction context.


Note:

Only use EntityManager method persist on a new entity. If you make changes to an existing entity, they are written to the database when the current transaction commits (see also "Using Flush").

Example 29-7 Creating a Entity with the EntityManager

@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession
{
    @PersistenceContext protected EntityManager entityManager;
...
    public void createEmployee(String fName, String lName)
    {
        Employee employee  = new Employee();
        employee.setFirstName(fName);
        employee.setLastName(lName);
        entityManager.persist(employee);
    }
...
}

Querying for an EJB 3.0 Entity Using the EntityManager

Using the EntityManager, you can:

For more information, see:

Finding an Entity by Primary Key with the Entity Manager

As Example 29-8 shows, f you know the primary key, you can use EntityManager method find to retrieve the corresponding entity from the database without having to create a query.

Example 29-8 Finding an Entity by Primary Key Using the EntityManager

@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession
{
...
    public void removeEmployee(Integer employeeId)
    {
        Employee employee = (Employee)entityManager.find("Employee", employeeId);
        ...
        entityManager.remove(employee);
    }
...
}

Creating a Named Query with the EntityManager

After you implement a named query (see "Implementing an EJB 3.0 Named Query"), you can acquire it at runtime using EntityManager method createNamedQuery as Example 29-9 Creating a Named Query with the EntityManagershows. If the named query takes parameters, you set them using Query method setParameter.

Example 29-9 Creating a Named Query with the EntityManager

Query queryEmployeesByFirstName = entityManager.createNamedQuery(
    "findAllEmployeesByFirstName"
);
queryEmployeeByFirstName.setParameter("firstName", "John");
Collection employees = queryEmployessByFirstName.getResultList();

Creating a Dynamic EJB QL Query with the Entity Manager

Example 29-10 shows how to create an ad hoc EJB QL query at runtime using EntityManager method createQuery.

Example 29-10 Creating a Dynamic Query Using the EntityManager

Query queryEmployees = entityManager.createQuery(
    "SELECT OBJECT(employee) FROM Employee employee"
);

Example 29-11 shows how to create an ad hoc query that takes a parameter named firstname using EntityManager method createQuery. You set the parameter using Query method setParameter.

Example 29-11 Creating a Dynamic EJB QL Query with Parameters Using the EntityManager

Query queryEmployees = entityManager.createQuery(
    "SELECT OBJECT(emp) FROM Employee emp WHERE emp.firstName = :firstname"
);
queryEmployeeByFirstName.setParameter("firstName", "John");

Creating a Dynamic TopLink Expression Query with the EntityManager

As Example 29-12 shows, using the oracle.toplink.ejb.cmp3.EntityManager method createQuery(Expression expression, Class resultType), you can create a query based on a TopLink Expression.

For more information, see "Understanding TopLink Expressions" in the Oracle TopLink Developer's Guide.

Alternatively, you can use javax.persistence.EntityManager and specify the TopLink Expression as a query hint (see "Configuring Query Hints").

Example 29-12 Creating a Dynamic TopLink Expression Query Using the Entity Manager

@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession
{
...
    public Collection findManyProjectsByQuery(Vector params)
    {
        ExpressionBuilder builder = new ExpressionBuilder();
        Query query = ((oracle.toplink.ejb.cmp3.EntityManager)em).createQuery(
            builder.get("name").equals(builder.getParameter("projectName")),
            Project.class
        );
        query.setParameter("projectName", params.firstElement());
        Collection projects = query.getResultList();
        return projects;
    }
...
}

Creating a Dynamic Native SQL Query with the EntityManager

Using the EntityManager method createNativeQuery(String sqlString, Class resultType), you can create a query based on a native SQL String that you supply as Example 29-13 shows.

Example 29-13 Creating a Dynamic Native SQL Query with the EntityManager

Query queryEmployees = entityManager.createNativeQuery(
    "Select * from EMP_TABLE where Salary < 50000", Employee.class
);


Note:

OC4J does not support EntityManager method createNativeQuery(String sqlString).

Example 29-14 shows how to create an ad hoc native SQL query that takes a parameter named salary using EntityManager method createNativeQuer(.(String sqlString, Class resultClass) You set the parameter using Query method setParameter.

Example 29-14 Creating a Dynamic Native SQL Query with Parameters Using the EntityManager

Query queryEmployees = entityManager.createNativeQuery(
    "Select * from EMP_TABLE where Salary < #salary", Employee.class
);
queryEmployeeByFirstName.setParameter("salary", 50000);

Configuring Query Hints

A query hint is name-value pair that you can use to configure a query with a vendor-specific option that is not available in the EJB 3.0 specification.

OC4J, using the TopLink persistence manager, provides the hints shown in Table 29-3.

Example 29-15 shows how to use Query method setHint to configure a named query to always refresh the TopLink cache from the database.

Example 29-15 Configuring a Query with a Hint

Query queryEmployeesByFirstName = entityManager.createNamedQuery(
    "findAllEmployeesByFirstName"
);
queryEmployeesByFirstName.setHint("refresh", new Boolean(true));

Table 29-3 Query Hints that OC4J Supports

Hint Name Hint Value Description

fetchSize

java.lang.Integer

Allows the user to set the fetch size of a TopLink query in bytes.

For more information, see "JDBC Fetch Size" in the Oracle TopLink Developer's Guide.

referenceClass

java.lang.Class

Overrides the target class of the query.

cacheUsage

java.lang.Integer

Specifies how the query uses the TopLink cache. Values are as defined for the following fields of oracle.toplink.queryframework.ObjectLevelReadQuery:

  • CheckCacheByExactPrimaryKey

  • CheckCacheByPrimaryKey

  • CheckCacheOnly

  • CheckCacheThenDatabase

  • ConformResultsInUnitOfWork

For more information, see:

refresh

java.lang.Boolean

Set to true if the TopLink cache should be refreshed from the database when this query executes.

For more information, see "Understanding the Cache" in the Oracle TopLink Developer's Guide.

lockMode

java.lang.Integer

Specifies whether or not the query uses pessimistic locking. Values are as defined for the following fields of oracle.toplink.queryframework.ObjectLevelReadQuery:

  • LOCK: issues SELECT .... FOR UPDATE.

  • LOCK_NOWAIT: issues SELECT .... FOR UPDATE NO WAIT.

  • NO_LOCK: pessimistic locking is not used.

  • DEFAULT_LOCK_MODE: fine grained locking will occur.

For more information, see the Oracle TopLink API Reference.

expression

oracle.toplink.expressions.Expression

Allows querying using the TopLink Expression API

For more information, see:

timeout

java.lang.Integer

Sets the query timeout in milliseconds.


Executing a Query

As Example 29-16 shows, to execute a query that returns multiple results, use Query method getResultList. This method returns a java.util.List.

Example 29-16 Executing a Query that Returns Multiple Results

Collection employees = queryEmployees.getResultList();

As Example 29-17 shows, to execute a query that returns a single result, use Query method getSingleResult. This method returns a java.lang.Object.

Example 29-17 Executing a Query that Returns a Single Result

Object obj = query.getSingleResult();

As Example 29-18 shows, to execute a query that updates (modifies or deletes) entities, use Query method executeUpdate. This method returns the number of rows affected (updated or deleted) as an int.

Example 29-18 Executing an Updating Query

Query queryRenameCity = entityManager.createQuery(
    "UPDATE Address add SET add.city = 'Ottawa' WHERE add.city = 'Nepean'"
);
int rowCount = queryRenameCity.executeUpdate();

Modifying an Entity Instance

You can modify an entity instance in one the following ways:

You must perform these operations within a transaction context. When the current transaction commits, your updates will be committed to the database.

You can also send updates to the database within a transaction before commit (see "Using Flush").

Using an Updating Query

Create an updating query (see "Creating a Named Query with the EntityManager" or "Creating a Dynamic EJB QL Query with the Entity Manager") and execute the query using the EntityManager (see "Executing a Query").

Using the Entity's Public API

Use the EntityManager to find or otherwise query for the entity (see "Querying for an EJB 3.0 Entity Using the EntityManager").

Use the entity's public API to change its persistent state.

Refreshing from the Database

As Example 29-19 shows, you can overwrite the current state of an entity instance with the currently committed state from the database using the EntityManager method refresh.

Example 29-19 Refreshing an Entity from the Database

@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession
{
...
    public void undoUpdateEmployee(Integer employeeId)
    {
        Employee employee = (Employee)entityManager.find("Employee", employeeId);
        em.refresh(employee);
    }
...
}

Removing an Entity

As Example 29-20 shows, you can use EntityManager method remove to delete an entity from the database.

Example 29-20 Removing an Entity

@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession
{
...
    public void removeEmployee(Integer employeeId)
    {
        Employee employee = (Employee)entityManager.find("Employee", employeeId);
        ...
        entityManager.remove(employee);
    }
...
}

Using Flush

As Example 29-21 shows, you can use EntityManager method flush to send updates to the database within a transaction before the transaction is committed. Subsequent queries within the same transaction will return the updated data. This is useful if a particular transaction spans multiple operations.

Example 29-21 Sending Updates to the Database within a Transaction

@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession
{
...
    public void terminateEmployee(Integer employeeId, Date endDate)
    {
        Employee employee = (Employee) entityManager.find("Employee", employeeId);
        employee.getPeriod().setEndDate(endDate);
        entityManager.flush();
    }
...
}

Detaching and Merging an Entity Bean Instance

An EntityManager is said to have a persistence context. When you create (see "Creating a New Entity Instance") or find (see "Querying for an EJB 3.0 Entity Using the EntityManager") an entity using an EntityManager instance, the entity is said to be part of the persistence context of that EntityManager.

While an entity is part of the persistence context of an EntityManager, it is said to be a persistent entity.

When an entity is no longer part of this persistence context, it is said to be a detached entity.

An entity is detached from the persistence context when the persistence context ends or when an entity is serialized (for example, to a separate application tier).

As Example 29-22 shows, you can use EntityManager method merge to merge the state of detached entity into the current persistence context of the EntityManager.

Example 29-22 Merging an Entity into the Persistence Context of an EntityManager

@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession
{
...
    public void updateAddress(Address addressExample)
    {
        entityManager.merge(addressExample);
    }
...
}

For more information about persistence context:

Accessing an EJB 3.0 EJBContext

For EJB 3.0 session and message-driven beans, you can access the EJBContext that OC4J provides (see "Using Resource Injection").

For more information, see:

Using Resource Injection

In an EJB 3.0 EJB client, you can use @Resource injection to access the EJBContext as Example 29-23 shows.

Example 29-23 Accessing EJBContext Using @Resource

@Resource SessionContext ctx;

Accessing an EJB 2.1 EJB

This section describes:

Accessing an EJB 2.1 EJB Remotely

A remote multitier situation exists when you have the servlets executing in one server which are to connect and communicate with EJBs in another server. Both the servlets and EJBs are contained in the same application. When you deploy the application to two different servers, the servlets normally look for the local EJB first.

In Figure 29-1, the HelloBean application is deployed to both server 1 and 2. In order to ensure that the servlets only call out from server 1 to the EJBs in server 2, you must set the remote attribute appropriately in the application before deploying on both servers.

Figure 29-1 Multitier Example

Description of Figure 29-1 follows
Description of "Figure 29-1 Multitier Example "

The remote attribute in the <ejb-module> element in orion-application.xml for the EJB module denotes whether the EJBs for this application are deployed or not.

  1. In server 1, you must set remote=true in the <ejb-module> element of the orion-application.xml file and then deploy the application. The EJB module within the application will not be deployed. Thus, the servlets will not look for the EJBs locally, but will go out to the remote server for the EJB requests.

  2. In server 2, you must set remote=false in the <ejb-module> element of the orion-application.xml file and then deploy the application. The application, including the EJB module, is deployed as normal. The default for the remote attribute is false; thus, simply ensure that the remote attribute is not true and redeploy the application.

  3. Configure RMI options:

    • In a standalone OC4J, specify RMI server data in the RMI configuration file, rmi.xml. Specify the location of this file in server.xml, the OC4J configuration file. By default, both these files are installed in <ORACLE_HOME>/j2ee/home/config.

      For more information, see "Configuring RMI in a Standalone OC4J Installation" in the Oracle Containers for J2EE Services Guide.

    • In an Oracle Application Server environment, you must edit the opmn.xml file to specify the port range on which this local RMI server listens for RMI requests. Note that manual changes to configuration files in an Oracle Application Server environment must be manually updated on each OC4J instance.

      For more information, see "Configuring RMI in an Oracle Application Server Environment" in the Oracle Containers for J2EE Services Guide.

  4. Set JNDI properties java.naming.provider.url and java.naming.factory.initial.

    For more information see:

  5. Look up the remote EJB.

    For more information, see:

    If multiple remote servers are configured, OC4J searches all remote servers for the intended EJB application.

For more information, see "Using Remote Method Invocation in OC4J" in the Oracle Containers for J2EE Services Guide.

Accessing an EJB 2.1 EJB Locally

A local multitier situation exists when both the servlets and EJBs are contained in the same application and deployed to the same server.

The remote attribute in the <ejb-module> element in orion-application.xml for the EJB module denotes whether the EJBs for this application are deployed or not.

  1. In the server to which you deploy your application, you must set remote=false in the <ejb-module> element of the orion-application.xml file and then deploy the application. The application, including the EJB module, is deployed as normal. The default for the remote attribute is false.

  2. Set JNDI properties java.naming.provider.url and java.naming.factory.initial.

    For more information see:

  3. Look up the local EJB.

    For more information, see:

Accessing an EJB 2.1 EJB Using RMI from a Standalone Java Client

Example 29-24 shows the type of look up that you can use from a standalone Java client (see "Standalone Java Client") in this release to look up an OC4J-deployed EJB without having to specify an RMI port. Example 29-24 shows how to look up the EJB named MyCart in the J2EE application ejbsamples deployed to the OC4J instance named oc4j_inst1 running on host myServer.

Example 29-24 Accessing an EJB 2.1 EJB Using RMI from a Standalone Java Client

Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,"oracle.j2ee.rmi.RMIInitialContextFactory");
env.put(Context.SECURITY_PRINCIPAL, "oc4jadmin");
env.put(Context.SECURITY_CREDENTIALS, "password");
env.put(Context.PROVIDER_URL,"opmn:ormi://myServer:oc4j_inst1/ejbsamples");

Context context = new InitialContext(env);

Object homeObject = context.lookup("MyCart");
CartHome home = (CartHome)PortableRemoteObject.narrow(homeObject,CartHome.class);

For more information, see:

Accessing an EJB 2.1 EJB in Another Application

Normally, you cannot have EJBs communicating across EAR files, that is, across applications that are deployed in separate EAR files. The only way for an EJB to access an EJB that was deployed in a separate EAR file is to declare it to be the parent of the client. Only children can invoke methods in a parent.

For example, there are two EJBs, each deployed within their EAR file, called sales and inventory, where the sales EJB needs to invoke the inventory EJB to check to see if enough widgets are available. Unless the sales EJB defines the inventory EJB to be its parent, the sales EJB cannot invoke any methods in the inventory EJB, because they are both deployed in separate EAR files. So, define the inventory EJB to be the parent of the sales EJB and the sales EJB can now invoke any method in its parent.

You can only define the parent during deployment with the deployment wizard. See the "Deploying/Undeploying Applications" section in the "Using the oc4jadmin.jar Command Line Utility" chapter in the Oracle Containers for J2EE Configuration and Administration Guide on how to define the parent application of a bean.

Accessing an EJB 2.1 MDB

A client never accesses an MDB directly: rather, the client accesses an MDB by sending a message through the JMS destination (queue or topic) associated with the MDB.

This section describes:

Sending a Message to a JMS Destination Using EJB 2.1

To send a message to a JMS destination using EJB 2.1:

  1. Look up both the JMS destination (queue or topic) and its connection factory.

    You can look up these resources using a predefined logical name (see "Configuring an Environment Reference to a JMS Destination or Connection Resource Manager Connection Factory (JMS 1.0)") or the explicit JNDI name you defined when you configured your JMS provider (see "Configuring an EJB 2.1 MDB to Use a Non-J2CA Message Service Provider").

    Oracle recommends that you use logical names as shown in this procedure and its examples.

  2. Use the connection factory to create a connection.

    If you are receiving messages for a queue, then start the connection.

  3. Create a session over the connection.

  4. Use the retrieved JMS destination to create a sender for a queue or a publisher for a topic.

  5. Create the message.

  6. Send the message using either the queue sender or the topic publisher.

  7. Close the queue session.

  8. Close the connection.

Example 29-25 shows how a servlet client sends a message to a queue.

Example 29-26 shows how a JSP client sends a message over a topic.

Example 29-25 Servlet Client Sends Message to a Queue

public final class testResourceProvider extends HttpServlet
{
  private String resProvider = "myResProvider";
  private HashMap msgMap = new HashMap();
  Context ctx = new InitialContext();

 public void doGet(HttpServletRequest req, HttpServletResponse res)
    throws ServletException, IOException
 {
    doPost(req, res);
 }

 public void doPost(HttpServletRequest req, HttpServletResponse res)
    throws ServletException, IOException
 {
   // Retrieve the name of the JMS provider from the request, which is to be used in creating
   // the JNDI string for retrieval
    String rp = req.getParameter ("provider");
    if (rp != null) resProvider = rp;

    try
    {
      // 1a. Look up the Queue Connection Factory
      QueueConnectionFactory qcf = (QueueConnectionFactory)
              ctx.lookup (
                  "java:comp/resource/" + resProvider + "/QueueConnectionFactories/myQCF"
              ); 
      // 1b. Lookup the Queue
      Queue queue = (Queue)
              ctx.lookup (
                  "java:comp/resource/" + resProvider + "/Queues/rpTestQueue"
              );
      
      // 2a. Create queue connection using the connection factory.
      QueueConnection qconn = qcf.createQueueConnection();
      // 2a. We're receiving msgs, so start the connection.
      qconn.start();

      // 3. create a session over the queue connection.
      QueueSession sess = qconn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);

       // 4. Since this is for a queue, create a sender on top of the session. 
       //This is used to send out the message over the queue.
      QueueSender snd = sess.createSender (q);

    drainQueue (sess, q);
    TextMessage msg = null;

    /* Send msgs to queue.  */
    for (int i = 0; i < 3; i++)
    {
      // 5. Create message
      msg = sess.createTextMessage();
      msg.setText ("TestMessage:" + i);

      // Set property of the recipient to be the MDB
      // and set the reply destination.
      msg.setStringProperty ("RECIPIENT", "MDB");
      msg.setJMSReplyTo(q);
      
      //6. send the message using the sender.
      snd.send (msg);

      // You can store the messages IDs and sent-time  in a map (msgMap),
      // so that when messages are received, you can verify if you 
      // *only* received those messages that you were
      // expecting. See receiveFromMDB() method where msgMap gets used
      msgMap.put( msg.getJMSMessageID(), new Long (msg.getJMSTimestamp()) );
    }

    // receive a reply from the MDB.
    receiveFromMDB (sess, q);

     //7. Close sender, session, and connection for queue
     snd.close();
     sess.close();
     qconn.close();
    }
    catch (Exception e)
    {
      System.err.println ("** TEST FAILED **"+ e.toString());
      e.printStackTrace();
    }
    finally
    {
    }
 }

  /*
   * Receive any msgs sent to us through the MDB
   */
  private void receiveFromMDB (QueueSession sess, Queue q)
    throws Exception
  {
    // The MDB sends out a message (as a reply) to this client. The MDB sets
    // the receipient as CLIENT. Thus, we will only receive msgs that have 
    // RECIPIENT set to 'CLIENT'
    QueueReceiver rcv = sess.createReceiver (q, "RECIPIENT = 'CLIENT'");

    int nrcvd = 0;
    long trtimes = 0L;
    long tctimes = 0L;
    // First msg needs to come from MDB. May take  a little while 
    //Receiving Messages
    for (Message msg = rcv.receive (30000); msg != null; msg = rcv.receive (30000))
    {
      nrcvd++;
      String rcp = msg.getStringProperty ("RECIPIENT");

      // Verify if msg in message Map  
      // We check the msgMap to see if this is the message that we are expecting.
      String corrid = msg.getJMSCorrelationID();
      if (msgMap.containsKey(corrid))
      {
        msgMap.remove(corrid);
      }
      else
      {
        System.err.println ("** received unexpected message [" + corrid + "] **");
      }
    }
    rcv.close();
  }

  /*
   * Drain messages from queue
   */
  private int drainQueue (QueueSession sess, Queue q)
    throws Exception
  {
    QueueReceiver rcv = sess.createReceiver (q);
    int nrcvd = 0;

    // First drain any old msgs from queue
    for (Message msg = rcv.receive(1000); msg != null; msg = rcv.receive(1000))
      nrcvd++;

    rcv.close();

    return nrcvd;
  }
}

Example 29-26 JSP Client Sends Message to a Topic

<%@ page import="javax.jms.*, javax.naming.*, java.util.*" %>
<%
//1a. Lookup the MessageBean topic
jndiContext = new InitialContext();
topic = (Topic)jndiContext.lookup("rpTestTopic");

//1b. Lookup the MessageBean Connection factory
topicConnectionFactory = (TopicConnectionFactory) jndiContext.lookup("myTCF");

//2 & 3. Retrieve a connection and a session on top of the connection
topicConnection = topicConnectionFactory.createTopicConnection();
topicSession = topicConnection.createTopicSession(true,Session.AUTO_ACKNOWLEDGE);

//5. Create the publisher for any messages destined for the topic
topicPublisher = topicSession.createPublisher(topic);

//6. Send out the message
for (int  ii = 0; ii < numMsgs; ii++)
{
  message = topicSession.createBytesMessage();
  String  sndstr = "1::This is message " + (ii + 1) + " " + item;
  byte[]  msgdata = sndstr.getBytes();
  message.writeBytes(msgdata);
  
  topicPublisher.publish(message);
  System.out.println("--->Sent message: " + sndstr);
}

//7. Close publisher, session, and connection for topic
topicPublisher.close();
topicSession.close();
topicConnection.close();
%>
Message sent!

Sending a Message to a J2CA Destination Using EJB 2.1

To send a message to a J2CA destination using EJB 2.1:

  1. Look up both the J2CA connection factory.

    You can look up this resource using a predefined logical name (see "Configuring an Environment Reference to a JMS Destination or Connection Resource Manager Connection Factory (JMS 1.0)") or the explicit JNDI name you defined when you configured your JMS provider (see "Configuring an EJB 2.1 MDB to Use a Non-J2CA Message Service Provider").

    Oracle recommends that you use logical names as shown in this procedure and its examples.

  2. Obtain a javax.resource.cci.ConnectionFactory.

    If the EIS is a JMS message service provider, there will likely be connection factory choices for queue or topic. For example, the Oracle JMS Connector offers a QueueConnectionFactory and a TopicConnectionFactory.

  3. Use the factory to obtain a javax.resource.cci.Connection.

  4. Use the connection to obtain a javax.resource.cci.Interaction.

  5. Configure the interaction and use Interaction method execute to send the message.

Accessing an EJB 2.1 EJBContext

For EJB 2.1 session, entity, and message-driven beans, you can access the EJBContext that OC4J provides by providing an appropriate get and set method when you implement your bean.

For more information, see:

Handling Parameters

This section describes:

Passing Parameters Into an EJB

When you implement an EJB or write the client code that calls EJB methods, you must be aware of the parameter-passing conventions used with EJBs.

A parameter that you pass to a bean method—or a return value from a bean method—can be any Java type that is serializable. Java primitive types, such as int, double, are serializable. Any non-remote object that implements the java.io.Serializable interface can be passed. A non-remote object that is passed as a parameter to a bean or returned from a bean is passed by value, not by reference. So, for example, if you call a bean method as follows:

public class theNumber {
   int x;
}
...
bean.method1(theNumber);

then method1() in the bean receives a copy of theNumber. If the bean changes the value of theNumber object on the server, this change is not reflected back to the client, because of pass-by-value semantics.

If the non-remote object is complex—such as a class containing several fields—only the non-static and non-transient fields are copied.

When passing a remote object as a parameter, the stub for the remote object is passed. A remote object passed as a parameter must extend remote interfaces.

The next section demonstrates parameter passing to a bean, and remote objects as return values.

Handling Parameters Returned by an EJB

The EmployeeBean getEmployee method returns an EmpRecord object, so this object must be defined somewhere in the application. In this example, an EmpRecord class is included in the same package as the EJB interfaces.

The class is declared as public and must implement the java.io.Serializable interface so that it can be passed back to the client by value, as a serialized remote object. The declaration is as follows:

package employee;

public class EmpRecord implements java.io.Serializable {
  public String ename;
  public int empno;
  public double sal;
}

Note:

The java.io.Serializable interface specifies no methods; it just indicates that the class is serializable. Therefore, there is no need to implement extra methods in the EmpRecord class.

Handling Exceptions

This section describes:

Recovering From a NamingException While Accessing a Remote EJB

If you are trying to remotely access an EJB and you receive an javax.naming.NamingException error, your JNDI properties are probably not initialized properly. See "Load Balancing" for a discussion on setting up JNDI properties when accessing an EJB from a remote object or remote servlet.

Recovering From a NullPointerException While Accessing a Remote EJB

When accessing a remote EJB from a Web application, you receive the following error: "java.lang.NullPointerException: domain was null ". In this case, you must set an environment property in your client while accessing the EJB set dedicated.rmicontext to true.

The following demonstrates how to use this additional environment property:

Hashtable env = new Hashtable( );
env.put (Context.INITIAL_CONTEXT_FACTORY,
  "oracle.j2ee.rmi.RMIInitialContextFactory");
env.put (Context.SECURITY_PRINCIPAL, "oc4jadmin");
env.put (Context.SECURITY_CREDENTIALS, "oc4jadmin");
env.put (Context.PROVIDER_URL, "ormi://myhost-us/ejbsamples");
env.put ("dedicated.rmicontext", "true"); // for 9.0.2.1 and later
Context context = new InitialContext (env);

See "Load Balancing" for more information on dedicated.rmicontext.

Recovering From Deadlock Conditions

If the call sequence of several beans cause a deadlock scenario, OC4J notices the deadlock condition and throws a Remote exception that details the deadlock condition in one of the offending beans.