Enterprise Java™Beans (EJBs) provide a powerful mechanism for distributed computing as well as container-managed persistence. The programming model, however, can be complex, cumbersome, and difficult to remember. This article shows you how to use IBM® WebSphere® Studio Application Developer V5.1.2 (hereafter called Application Developer) to automate the task of programming with EJBs, by generating the client code and hiding the complexity.
While automated code generation is great for productivity, it is always beneficial for the programmer to understand what is going on behind the scenes. This article will show you a few different ways to program with EJBs by hand, as you've had to do it in the past. Unfortunately, not all methods for EJB lookup are created equal, and there are real-world production applications that use all of the methods described, even though some of them are not recommended. The examples below progress from bad to better to even better. Finally, the article walks you through the best and easiest method for EJB lookup and optimization.
You can download the example code in the form of a Project Interchange ZIP file, which you can then import into your Application Developer workspace. Doing so will enable you to follow along with the article and evaluate the sample code. In Application Developer V5.1.2, the Project Interchange feature, which lets you import the content for this article, is now included in the base product. For information on how to use the Project Interchange import wizard and a feature download for earlier versions of WebSphere Studio, see Share and share alike: A new Project Interchange feature for Eclipse and WebSphere Studio. In order to take advantage of the new Snippets feature, you need Application Developer V5.1.2 or later.
The example project interchange ZIP file contains four projects: SnippetsEAR and SnippetsEJB,
which together compose a server based EJB application, and
SnippetClientEAR and SnippetAppClient, which together compose an application client that will use
the EJBs from the first application. After you import the two projects, your view of the workspace from the J2EE Hierarchy view should look like this:
The SnippetsEJB project contains one entity bean, and four session beans. The entity bean is a very simple example, named Person,
which has attributes firstName and lastName, and a key field named id.
The Person bean also contains a custom create method on the home interface, which we will use for the example lookups. Each of the four session beans demonstrates a slightly different way to lookup the Home interface for the Person bean
and then to invoke the create method.
The first example uses a direct lookup on the JNDI name of the referenced bean. This is how you would perform the lookup without EJB references (discussed later).
All EJBs must be bound to a JNDI name on the application server on which they are hosted. As of the EJB specification V1.1 or later,
the JNDI name itself is not part of the standard ejb-jar.xml deployment descriptor that is part of the EJB specification.
Instead, it is stored in a vendor-specific file that is used by the EJB container (application server) at run time. On WebSphere Application Server, the JNDI names
of EJBs are stored in the bindings file named ibm-ejb-jar-bnd.xmi, under the directory META-INF.
The file is a peer to the ejb-jar.xml deployment descriptor (DD), and contains references to the EJB objects defined in the DD. Under normal circumstances you should not need to hand edit this file. The JNDI names for the EJBs can be added using any of the following methods:
- Let Application Developer create a default JNDI name whenever a new EJB is created.
- Use the Deployment Descriptor Editor to set or change the JNDI name.
- Set the JNDI names as part of installing a J2EE application on the server.
In the example, we have chosen the first method for creating default JNDI names. No action was required on our part; the values were set by the EJB creation wizard. To see the JNDI name for the Person bean, double-click on the bean in the J2EE Hierarchy view, which
takes you to the Beans page of the editor. Notice the WebSphere bindings section on the right side of the page:
By convention, the JNDI names for all EJBs should start with the ejb segment.
The following method exists in the session bean implementation class LookupWithJNDIBean.java. This code uses a hard-coded
reference to the JNDI name to perform the EJB lookup:
public void createNewPerson(Integer id, String firstName, String lastName) {
Object obj = null;
try {
InitialContext ctx = new InitialContext();
obj = ctx.lookup("ejb/sample/ejb/PersonHome");
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
PersonHome aPersonHome =(PersonHome) PortableRemoteObject.narrow(obj, PersonHome.class);
Person aPerson = null;
try {
aPerson = aPersonHome.create(id, firstName, lastName);
} catch (RemoteException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (CreateException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
} |
This code first creates a new InitialContext using the default constructor. The InitialContext will be constructed with all its properties preset by the application server run time. It then uses the InitialContext to perform a JNDI lookup for the object registered with the JNDI name ejb/sample/ejb/PersonHome.
In this case, that object will be the remote home interface for the Person bean. We then must cast the interface using the
PortableRemoteObject#narrow API. Lastly, we can use the casted home interface to invoke our custom create method.
Here are reasons not to use the above method for looking up EJBs:
- The JNDI name is hard-coded in the Java code. This makes the Java code much less portable. If you need to change the JNDI name of a bean to eliminate a conflict, or because you are deploying to another app server, then you must also update and recompile the Java code. This can be tedious and expensive.
- Using this implementation code, only "remote" interfaces can be used. This adds quite a performance penalty for clients running on the same server (such as other EJBs in the same EAR, Web applications, and so on). For more information, see "Client View of an Entity" in the EJB specification.
Lookup 2: Factoring the lookups
The next example introduces a slight improvement over the first method, in that we factor out the common lookup code into a separate method. The following code is illustrated in the Session bean class LookupWithServiceMethodBean.java:
private Object lookupRemoteHome(Class aHomeClass, String jndiName) {
Object obj = null;
try {
InitialContext ctx = new InitialContext();
obj = ctx.lookup(jndiName);
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return PortableRemoteObject.narrow(obj, PersonHome.class);
} |
This cleans up the code in the method that actually retrieves and uses the bean, thus making the code a bit less cluttered and more readable, as shown below:
public void createNewPerson(Integer id, String firstName, String lastName) {
PersonHome aPersonHome = (PersonHome)lookupRemoteHome(PersonHome.class,
"ejb/sample/ejb/PersonHome");
Person aPerson = null;
try {
aPerson = aPersonHome.create(id, firstName, lastName);
} catch (RemoteException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (CreateException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
} |
Moreover, now the lookup method is available for reuse within this class for lookups of other referenced beans. However, like the previous example, the same disadvantages remain:
- The JNDI name is a hard-coded.
- Only remote interfaces can be used.
Lookup 3: Using an EJB reference
This example shows you how to use EJB references to look up the client interface for the Person bean and invoke the custom create method. EJB references let us use an extra level of indirection between the code and the actual JNDI name of the referenced bean. For illustrative purposes, we will show how to use this pattern for both local and remote client views. Whenever possible, however, use local client views and EJB local references.
Create an EJB reference from a session bean to the Person CMP bean:
- From the J2EE Hierarchy view, in the J2EE Perspective, select the session bean
LookupWithReference. - Select New -> Reference.

- Select EJB Reference, and select Next.
- Select Enterprise bean in current EJB project, and then select the
Personbean. The EJB reference name defaults automatically toejb/Person.
- Select Finish.
The following code has been added to the file ejb-jar.xml:
<ejb-ref id="EjbRef_1097813639528">
<ejb-ref-name>ejb/Person</ejb-ref-name>
<ejb-ref-type>Entity</ejb-ref-type>
<home>sample.ejb.PersonHome</home>
<remote>sample.ejb.Person</remote>
<ejb-link>Person</ejb-link>
</ejb-ref>
|
The JNDI name of the referenced EJB was defaulted for you. From the J2EE Hierarchy View, double-click on the bean
LookupWithReference, then turn to the References page of the editor:
This JNDI name is the JNDI name of the linked EJB, Person. Now we have a mechanism for looking up a referenced
bean without hard coding the JNDI name in Java code. Here is the improved implementation of the lookup example in the class
LookupWithReferenceBean.java:
public void createNewPersonUsingRemote(Integer id, String firstName, String lastName) {
PersonHome aPersonHome = (PersonHome)lookupRemoteHome(PersonHome.class, "ejb/Person");
Person aPerson = null;
try {
aPerson = aPersonHome.create(id, firstName, lastName);
} catch (CreateException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private Object lookupService(String referenceName) {
try {
Context aContext = new InitialContext();
return aContext.lookup("java:comp/env/" + referenceName);
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
private EJBHome lookupRemoteHome(Class aHomeClass, String refName) {
Object remote = lookupService(refName);
return (EJBHome)PortableRemoteObject.narrow(remote, aHomeClass);
} |
Notice that instead of passing in the JNDI name from the caller, we now use the EJB reference name ejb/Person to perform the lookup.
As in the previous example, we have factored out the service lookup code, in order to keep the createNewPerson method,
which does the real work, as simple as possible. Factoring out the lookup code also lets us reuse it for other EJB references.
Similarly, you can use the EJB reference wizard to create an EJB local reference, in which case the code looks like this:
private EJBLocalHome lookupLocalHome(String refName) {
return (EJBLocalHome) lookupService(refName);
}
public void createNewPersonUsingLocal(Integer id, String firstName, String lastName) {
PersonLocalHome aPersonLocalHome = (PersonLocalHome) lookupLocalHome("ejb/PersonLocal");
PersonLocal aPerson = null;
try {
aPerson = aPersonLocalHome.create(id, firstName, lastName);
} catch (CreateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} |
In this example, it is not necessary to narrow the result to the home class, since no RMI calls are made when using local interfaces. Here are two improvements in this method of lookup vs. using JNDI names directly:
- We are coding to the EJB reference name as opposed to the JNDI name of the target bean. This allows for late binding of the
JNDI names in an EJB application without having to recompile the Java code. Notice the use of the
java:comp/env/scheme, which is appended to the front of the EJB reference name to construct the lookup string. This is a special scheme which is used by the Naming service of the EJB container to resolve the reference by name to the actual JNDI object. - The second example takes advantage of the local client view of the target (
Person) EJB. We can do that since the client in this case is another EJB running on the same server. This eliminates any RMI transfer and is a significant advantage of EJB 2.0 or later.
Here are two disadvantages that still remain in this example:
- Unless you write this code frequently, it can be hard to remember the format of the
java:comp/env/scheme. We've mitigated that to some extent by factoring out thelookupService()method and passing just the reference name. - The common lookup code that we factored must be duplicated in every client where we want to use this pattern.
Now that you've seen all the hard ways to code EJBs, the next section will show you how to turbo-charge your productivity and optimize your application.
Using snippets to simplify EJB lookups
So far we have seen methods for doing the lookup of the home interface of an EJB. Some of these methods were not very good and others were a bit better, but all required a good bit of hand coding. The better solutions required changes to the EJB deployment descriptor to add an EJB reference. Even though these approaches are better, they require you to remember to make those deployment descriptor changes. Ideally, you'd like to have the EJB reference created when it was needed -- when a method is coded that uses the EJB reference to look up the home interface.
This is where EJB snippets make it much easier to call the create and find methods for a home interface
of an EJB. The EJB snippet support builds on top of EJB references by providing the following benefits:
- EJB reference creation while adding the required code to look up the home interface
- Wizard-driven approach to add the necessary lookup code, which can be hard to remember
- Use of the service locator design pattern to simplify lookup code
- Optional EAR-level caching of home interfaces (more on this topic below).
Just by using a handful of mouse clicks, you can insert well-factored code that provides simplified lookups of home interfaces as
well as invoking a selected method. The following sections describe a similar example as the previous session beans, except that we
will invoke the EJB snippet framework to insert the necessary code in the LookupWithSnippets session bean class.
Application Developer adds a Snippets view that contains several compartments for the types of snippets that can be invoked. The J2EE perspective has this view in the lower left tabbed area as shown below. To open this view, select Window => Show View => Other and then select Basic => Snippets.
All of the compartments shown in this view are used in HTML files except for the EJB compartment, which can only be used from within
.java files that are contained within a project that is associated with an enterprise application project. The Snippets view provides function similar to the Java template support within Eclipse except that the Snippets view provides a means to visually represent the available templates. Also, the snippets that are invoked can invoke an advanced wizard that allows for object selections to drive the template creation which can span methods and fields to support the inserted code snippet. Currently the advanced snippet support is only invoked from the EJB compartment actions.
The EJB Snippet support makes it easy to invoke either create or find methods on a home interface. We will walk through an example of creating the same createNewPerson(...) method that was created using other well known schemes for doing EJB home lookups.
Call EJB create method Snippet wizard
- Open the Java editor on the
LookupWithSnippetsBeanJava file. - Create the following method signature with the cursor at the first line of the method body.
public void createNewPerson(Integer id, String firstName, String lastName) { } - Expand the EJB compartment of the Snippets view and double click on the Call an EJB
createmethod.
This will launch the EJB snippet wizard. - The first page of the wizard requires you to select the appropriate EJB reference that refers to the EJB home interface
that you would like to invoke. The tree displayed shows all of the available EJB reference owners within the workspace
(e.g., EJBs, Web applications, and Application Clients). Based on the Java file that was selected a good guess at the proper owner
is made, thus setting the selection to this guessed owner. In our example the
LookupWithSnippetsEJB is selected. It is your responsibility to select an EJB reference from the proper owner.
- Since there is not an EJB reference to the
PersonCMP bean, click New to invoke the EJB Reference creation wizard. Walk through this wizard to create an EJB Local Reference to thePersonbean, as outlined in the above section Using an EJB reference. - The new reference will now appear in the tree. Double click on this EJB reference.

- You will now be taken to the methods page which displays the available create methods. Double click on the create(Integer, String, String) method.

- The last page lets you modify the text that will be inserted for parameter values of the method. By default,
the values are the same names as the parameters of the method itself but you are free to change them. The values entered
are just simple strings that are inserted into the code snippet as entered on this page, so you can enter any code that you wish.

- Click Finish to insert the code.
- The inserted code may have compile errors due to missing import statements. To remove these errors select Ctrl + shift + o to organize imports.
EJB create method snippet wizard output
The following code is inserted at the cursor location. The PersonLocalHome lookup is done first and then the
create(...) method is invoked to create a new PersonLocal. The inserted code is formatted based on the formatting rules that are defined in the Java preferences. If the code is not formatted as you wish, change your Java formatting preferences to suit your needs.
PersonLocalHome aPersonLocalHome =
(PersonLocalHome) ServiceLocatorManager.getLocalHome(
STATIC_PersonLocalHome_REF_NAME,
STATIC_PersonLocalHome_CLASS);
PersonLocal aPersonLocal = null;
try {
if (aPersonLocalHome != null)
aPersonLocal = aPersonLocalHome.create(id, firstName, lastName);
} catch (CreateException ce) {
// TODO Auto-generated catch block
ce.printStackTrace();
} |
In order to support the lookup of the PersonLocalHome interface, two new static fields have been added to the class as well.
The first field holds the EJB reference name for the EJB reference that was selected in the EJB snippet wizard.
private final static String STATIC_PersonLocalHome_REF_NAME =
"ejb/Person"; |
The second static field holds the Class of the PersonLocalHome interface.
private final static Class STATIC_PersonLocalHome_CLASS =
PersonLocalHome.class; |
Addition of the serviceLocatorMgr.jar
One item in this code snippet that should raise some eyebrows is the reference to the ServiceLocatorManager class. Before we get into a
detailed explanation of this class let's first explore how the LookupSnippetsBean.java file has a reference to it in its build path. This class is
not a hidden class in one of the WebSphere Application Server JARs -- it is actually found within a serviceLocatorMgr.jar file,
which is located in the SnippetsEAR project. The steps below spell out what happened when you clicked Finish
in the EJB Snippet wizard in addition to inserting the code snippet and fields:
- The
serviceLocatorMgr.jaris added to each enterprise application project in which the current Java file is a member. - A classpath entry is added to the Java project's build path of the current Java file, which resolves to the
serviceLocatorMgr.jarso the inserted code will compile. - The file
MANIFEST.MFis modified for the Java project in Step 2 to include the following entry:Class-Path: serviceLocatorMgr.jarThis ensures that theServiceLocatorManagerreference will be available at run time.
Since the initial release of Snippets feature, important updates have been made to the serverLocatorMgr.jar file. These include an option to turn caching on and off (the default is now off), and the ability to set default properties for all Contexts. You can download the updated JAR file below and apply it to
your installation of application developer. Download serviceLocatorMgrUpdate.zip
and unzip it in the root installation directory for Application Developer.
The samples at the beginning of this article presented different alternatives for doing EJB home interface lookups. All of these samples shared one thing in common: they all used the same basic logic to perform the lookup. The EJB snippet sample clearly does not have this basic logic.
The ServiceLocatorManager encapsulates the logic necessary for looking up an EJB home interface and presents simple APIs for looking up
both local and remote home interfaces. The APIs for looking up home interfaces are shown below:
public static EJBHome getRemoteHome(String ejbReferenceName, Class homeClass) public static EJBHome getRemoteHome(String ejbReferenceName, Class homeClass, String providerURL, String nameserviceType) public static EJBLocalHome getLocalHome(String localEjbReferenceName, Class homeClass) public static EJBLocalHome getLocalHome(String localEjbReferenceName, Class homeClass, String providerURL, String nameserviceType) |
Next we will walk through the API code path within the ServiceLocatorManager that was used in our code snippet.
- First we start with
ServiceLocatorManager.getLocalHome( STATIC_PersonLocalHome_REF_NAME, STATIC_PersonLocalHome_CLASS). - It calls
getLocalHome(localEjbReferenceName, homeClass, null, null), where the last two parameters are the provider URL and the Name Service type, respectively. In this case null is used for both, because we want to use the default InitialContext. The default InitialContext will be initialized with no environment properties, unless the default environment properties are set using thesetDefaultProperties(Hashtable)method. - We then call
getHome(localEjbReferenceName, homeClass, providerURL, nameserviceType, false), where the last boolean indicates whether it is a remote call (in this example it is not). - The
getHome(...)method determines if synchronization is required. The test for synchronization depends on whether the returned Homes are being cached. Caching is discussed below. - The
doGetHome(...)method is the most interesting method, because it is where most of the work is done. The first check is to see if the home interface is already cached in the static HOMES Map only if caching is turned on and the default Context is being used. If the Home is found in the cache, it is returned; if not, the home interface must be looked up and then cached, (again, only if caching is enabled):private static Object doGetHome(String ejbReferenceName, Class homeClass, Context ctx, boolean isRemote, boolean useCache) { String homeName = null; Object home = null; if (useCache) { homeName = homeClass.getName(); home = HOMES.get(homeName); } if (home == null) { if (isRemote) home = lookupRemoteHome(ejbReferenceName, homeClass, ctx); else home = lookupLocalHome(ejbReferenceName, ctx); if (useCache && home != null) HOMES.put(homeName, home); } return home; } - The IntialContext is obtained in Step 4 by calling the
getInitialContext(String, String)method. If noproviderURLornameserviceTypeis provided, then the default InitialContext will be obtained. If caching is enabled the cached default InitialContext is obtained:/** * Return a Context based on only the Provide URL and the InitialContext factory Type * environment properties that are being passed. * Note, if the parameters passed are null, the default Context will be * returned; otherwise, a new InitialContext will be created with these * properties. * @param providerURL * @param nameserviceType * @return * * @see #getInitialContext() * @see Context#PROVIDER_URL * @see Context#INITIAL_CONTEXT_FACTORY */ public static Context getInitialContext(String providerURL, String nameserviceType) { Hashtable environment = setupEnvironmentHashtable(providerURL, nameserviceType); if (environment == null) return getInitialContext(); return getInitialContext(environment); }
- After the InitialContext is obtained, the method called from Step 5 does the lookup. In our code snippet,
lookupLocalHome(...)is called, since we are looking up a local home interface:private static EJBLocalHome lookupLocalHome(String localEjbReferenceName, Context ctx) { if (localEjbReferenceName != null) return (EJBLocalHome) lookupService(localEjbReferenceName, ctx); return null; }
It then calls the following to determine if synchronization is required. This is the same test used in Step 4. If caching is required, we synchronize the actual lookup block of code:
private static Object lookupService(String referenceName, Context aContext) { if (shouldUseHomeCache(aContext)) { synchronized (aContext) { return doLookupService(referenceName, aContext); } } else return doLookupService(referenceName, aContext); }
The actual lookup of the home is then performed in the
doLookupService(...)method:private static Object doLookupService(String referenceName, Context aContext) { try { return aContext.lookup("java:comp/env/" + referenceName); } catch (NamingException e) { getErrorHandler().handleLookupFailure(e, referenceName); } return null; } - If the
lookupRemoteHome(...)method was called, we would have gone down thelookupRemoteHome(...)method, which is the same as Step 7, except it does the narrowing for you, as shown below:private static EJBHome lookupRemoteHome(String ejbReferenceName, Class homeClass, Context context) { if (ejbReferenceName != null && homeClass != null) return (EJBHome) PortableRemoteObject.narrow(lookupService(ejbReferenceName, context), homeClass); return null; }
ServiceLocatorManager error handling
In order to keep the code that uses the ServiceLocatorManager simple, none of the APIs have exceptions in their throws clause,
so that all calls are not required to catch the exceptions and perform the same logic to tell the user that a problem has occurred.
This does not mean that the ServiceLocatorManager catches and ignores all exceptions. On the contrary, it catches exceptions and
pass the handling of them to a registered ServiceLocatorErrorHandler instance.
The ServiceLocatorErrorHandler interface has the following two APIs. The first method is called when there is a problem creating an
InitialContext. The second method is called when a lookup call on the InitialContext fails to find a home interface:
/**/** * @param e * @param properties */ void handleInitialContextFailure(NamingException e, Hashtable properties); /** * @param e * @param referenceName */ void handleLookupFailure(NamingException e, String referenceName); |
The ServiceLocatorErrorHandler has to be registered with the ServiceLocatorManager prior to using any APIs on the ServiceLocatorManager. The following method is used to register the ServiceLocatorErrorHandler.
If no ServiceLocatorErrorHandler is registered when an exception occurs, a default ServiceLocatoryErrorHandler
is constructed that simply prints out the stack trace of the exception:
/**
* Set the ServiceLocatorErrorHandler for this manager.
* @param handler
*/
public static void setErrorHandler(ServiceLocatorErrorHandler handler) {
ERROR_HANDLER = handler;
} |
Using EJB snippets from an application client
The basic usage of the EJB snippet support is the same no matter where it is called from. However, some unique changes take place when using the EJB snippet wizard to refer to an EJB within another enterprise application. The key difference here is that you cannot create an EJB reference with an EJB link defined, because this is allowed only for EJB references within the same enterprise application.
EJB client JAR project preference
Before starting, turn on a preference that allows the creation of an EJB client JAR project:
- Select Window > Preferences > J2EE.
- Select the options Create EJB client JAR projects for new EJB projects and the Use EJB Client JAR, if it exists:

- Select OK.
Using the EJB Snippet wizard from application client
We will create the same reference that was used in the Call EJB create method Snippet wizard,
except that we will do it from the snippet.example.MyMain.java file within the SnippetAppClient module.
- Open a Java editor on
snippet.example.MyMain.javaand put your cursor in themainmethod. - From the Snippets view, double-click on the Call an EJB
createmethod snippet action . - Select the SnippetAppClient from the tree and click New .
- This launches the Add EJB Reference wizard, as shown in the above section Using an EJB reference.
On the first page, select the Enterprise bean in different EAR option. Then select the Person CMP bean and click Finish:

- You will see the following prompt to have an EJB client JAR project created for the SnippetEJB project. Select Yes
to have the client project created:

- Double-click on the new ejb/Person reference.
- This advances you to the EJB Lookup Properties page, where you can enter the provider URL and name service type class
that will be used when creating the InitialContext. These properties may be necessary to look up the EJB home from the application client.
This page appears because the EJB reference that was selected does not contain an
ejb-linkattribute. For this example, we will select the Use default context properties for EJB home interface lookups. option, which indicates that we are running this application client on the same server as the target EJB module's EAR file. Therefore a default InitialContext can be used. If this was not the case, then you would set the Provider URL, which is a URL to the service provider (for exampleldap://somehost:389) and the Name Service Type, which is the class name of the initial context factory class (for example,com.ibm.websphere.naming.WsnInitialContextFactory):
- Select Next.
- Follow Steps 7 through 10 from the above section Using an EJB reference.
Application client-based EJB Snippet wizard output
Before we explain the output from this wizard, we need to explain what happened after Step 5 above. At this point, an EJB client JAR project is created for the
SnippetsEJB project called SnippetsEJBClient. The client-specific files (EJB client interfaces and their references) are then copied from the SnippetsEJB project to the SnippetsEJBClient project. This new project is then added to SnippetsEAR application as a project utility JAR.
EJB client JAR files must be placed in the EAR with the EJB module if the EJB module has a reference to it.
You can see this on the Modules tab of the EAR Deployment Descriptor of SnippetsEAR, as shown below:
The EJB deployment descriptor for the SnippetsEJB module project is also updated to have the ejb-client-jar
reference to this new SnippetsEJBClient.jar from the EAR file.
<ejb-client-jar>SnippetsEJBClient.jar</ejb-client-jar> |
The SnippetClientEAR also has a new project utility JAR entry to the SnippetsEJBClient project,
since it is now needed by the SnippetClient module project.
Lastly, a JAR dependency is set up for both the SnippetsEJB project and the SnippetClient project to point to the SnippetsEJBClient.jar entry. This ensures that the SnippetEJBClient project is added to the Java build path of both of these projects, and and that the SnippetEJBClient.jar entry is added to the MANIFEST.MF file
for both projects. This makes the contents of SnippetsEJBClient.jar available at run time.
After selecting Finish in the Snippet wizard from the MyMain.java file, the output is virtually the same as it was the first time
that we ran the wizard. First, ServiceLocatorMgr.jar is added to SnippetClientEAR,
and a Java JAR dependency is added to it from the SnippetClient module project,
just as it was in the section Addition of the serviceLocatorMgr.jar.
The following code is inserted at the cursor location. The PersonHome remote home lookup is done first, and then the
create(...) method is invoked to create a new Person:
PersonHome aPersonHome =
(PersonHome) ServiceLocatorManager.getRemoteHome(
STATIC_PersonHome_REF_NAME,
STATIC_PersonHome_CLASS);
Person aPerson = null;
try {
if (aPersonHome != null)
aPerson = aPersonHome.create(id, firstName, lastName);
} catch (CreateException ce) {
// TODO Auto-generated catch block
ce.printStackTrace();
} catch (RemoteException re) {
// TODO Auto-generated catch block
re.printStackTrace();
} |
In order to support the lookup of the PersonHome interface, two new static fields have been added to the class as well.
The first field holds the EJB reference name for the EJB reference that was selected in the EJB Snippet wizard:
private final static String STATIC_PersonHome_REF_NAME = "ejb/Person"; |
The second static field holds the class of the PersonHome interface:
private final static Class STATIC_PersonHome_CLASS = PersonHome.class; |
If you require additional variability with the environment properties, you can modify the generated snippet code to pass an instance of a hashtable that includes the context properties that you wish to set. For example, you can update the snippet above to look like this:
Hashtable properties = new Hashtable();
properties.put(Context.SECURITY_AUTHENTICATION, "strong");
PersonHome aPersonHome =
(PersonHome) ServiceLocatorManager.getRemoteHome(
STATIC_PersonHome_REF_NAME,
STATIC_PersonHome_CLASS,
properties);
Person aPerson = null;
try {
if (aPersonHome != null)
aPerson = aPersonHome.create(id, firstName, lastName);
} catch (CreateException ce) {
// TODO Auto-generated catch block
ce.printStackTrace();
} catch (RemoteException re) {
// TODO Auto-generated catch block
re.printStackTrace();
} |
Caching within the ServiceLocatorManager
The idea of caching within the ServiceLocatorManager was touched on earlier in this article. Before explaining how to enable caching,
a brief warning is required. Caching in ServiceLocators has been popular in the past with J2EE 1.2, because you could cache on the global JNDI name.
This article has already described the shortcomings of using the global JNDI name, but now that reference names are used for doing lookups of EJBs,
it is no longer possible to cache based on this reference name. Because reference names are scoped to application clients, Web applications, and individual EJBs,
the names are no longer global and are therefore not safe for caching. For more information on why most service locators fail, see
Eliminate caching in service locator implementations
in J2EE 1.3.
The ServiceLocatorManager employed in the EJB Snippet support does not have caching by default,
but you can enable caching by calling:
ServiceLocatorManager.setEnableDefaultHomeCaching(true) |
So what happens when caching is enabled? The default InitialContext is cached to avoid obtaining it each time, and any lookup call done via this default context is synchronized. Also, each Home instance is cached once found, so that subsequent lookups will obtain it from the cache, and this cache access is protected with a synchronized block. This caching happens only when the default Context is used. When other Contexts are used, no caching of the Homes is performed. Each Home instance is cached by the fully qualified name of the Home interface. This is safe for most applications, but there is one case in which the caching will not return the proper results and that is when there is a reference to two separate EJBs that share client interfaces. For example:
Client1
ejbs/A -> A
Client2
ejbs/B -> B
A
ABean
A
AHome
B
BBean
A
AHome |
In this situation, if caching is on, when the client does a look-up using ejbs/A it will obtain the correct AHome from bean A.
But then when Client2 does a look-up for ejbs/B, it will get the cached A bean's Home instance, because it was in the cache first with the AHome class name. This situation is rare, but if it does exist, you cannot enable caching. Also, only Home instances are being cached and not other types of references,
such as resource references. The ServiceLocatorManager is intended to support the EJB Snippets and thus only deals with EJB references. To flush the cache:
ServiceLocatorManager.flushCache() |
Leave caching off unless you have a performance issue with the Home lookups and you don't have the limiting case described above. If many lookups are being performed, enabling caching will yield a noticeable performance gain; otherwise performance gains are negligible.
If you wish to enable caching, profile your application in your particular environment to determine the maximum benefit. Many application servers, including WebSphere Application Server, support caching of references at the server level, thus making the performance gains of caching at the application level less dramatic than in the past.
This article has explored the basic programming idioms for looking up enterprise beans in an EJB application. It showed you some not-so-good practices and explained why to avoid them. It compared a few methods for lookup and explained why each was better than the previous one. Next the article introduced you to the Snippets view in Application Developer and showed you how to automate the generation of best practice coding idioms, as well as create EJB references "on the fly" as they are needed. It presented a pattern-based solution for lookups via the "service locator manager", and explained how this code works. Finally, the article showed you a simplified way to reference EJBs in another EAR file and automate the task of the EJB client JAR creation for referencing applications. Use of the techniques presented in this article should yield the following benefits for your development team:
- Improved productivity in developing EJB applications
- Better run-time performance for EJB applications
- Simplified, easy-to-read, easy-to-understand code
- Well-structured application packaging for multiple enterprise applications
| Name | Size | Download method |
|---|---|---|
| snippets_examples.zip | 20 KB | FTP |
| serviceLocatorMgrUpdate.zip | 9 KB | FTP |
Information about download methods
-
EJB development made easy using the UML Visualizer in WebSphere Studio
-
Share and share alike: A new Project Interchange feature for Eclipse and WebSphere Studio
-
Eliminate caching in service locator implementations in J2EE 1.3
-
Design Patterns: Service Locator by Sun Microsystems, Inc.
-
Enterprise JavaBeans Specification Version 2.0 by Sun Microsystems, Inc. Final Release: August 14, 2001
Ritchie Schacher is a software engineer at IBM Research Triangle Park Lab in Durham, North Carolina. He is a developer of J2EE tools for WebSphere Studio Application Developer. You can reach Ritchie at schacher@us.ibm.com.
Daniel Berg is a software engineer at IBM Research Triangle Park Lab in Durham, North Carolina. At the time of writing this article he was the team lead architect for J2EE tools in WebSphere Studio Application Developer. You can reach Dan at danberg@us.ibm.com.
Comments (Undergoing maintenance)





