WebObjects and J2EE: A Complementary Approach
Kelly Kazem, Sr. Systems Engineer, Apple Computer, Inc.
Introduction
WebObjects 5 is a mature, highly robust application development platform for
rapid development of enterprise-class three-tier Web and Java Client
applications and two-tier client/server-style applications. Java 2 Platform,
Enterprise Edition (J2EE) has recently emerged as a popular alternative for
building two- or three-tier Web-
based applications. Because both
WebObjects and J2EE are based
on Sun’s Java 2 Standard Edition
(J2SE) technology, the opportunity
exists for both to interoperate on
several levels.
WebObjects was one of the first
application servers to market. Its
technology is based on one of the first truly object-oriented development
platforms (NextStep/OpenStep). WebObjects has the capability of producing truly
scalable mission-critical applications. Driving its robustness is a well-designed,
high-level set of object frameworks, which form a solid foundation on which to
build complex business applications. WebObjects 5 is implemented as a 100
percent pure set of Java classes, currently based on JDK 1.3.
J2EE is still an emerging
technology, but nonetheless has
shown robust success in the
marketplace, at least in part due to
successful marketing by Sun. The
primary technologies in J2EE are
Enterprise JavaBeans (EJB),
JavaServer Pages (JSP), Java
Servlets, the Java Naming and
Directory Interface
(JNDI), the
Java Transaction API (JTA), the
Java Messaging Service API
(JMS), CORBA, and the JDBC
data access API.
Why Integrate?
Why even think about integrating these two competing technologies? After all,
they do compete from a marketing perspective, and they both enable the
development of scaleable, web-based applications and both support a three-tier
architecture to allow separation of pure business logic from presentation logic.
But despite these similarities, there are several valid reasons to consider
integrating WebObjects and J2EE.
One of the fundamental reasons for integrating the two technologies is to
leverage the benefits that come with reuse. When existing frameworks are
available to development teams building critical applications, there are many
reasons to consider using them,
including the cost and time develop
new frameworks from scratch. In
larger organizations that have
development teams using both
technologies, the ability to reuse and
share proven code is especially
important. Such repurposing of code
would once have been either
problematic or impossible, but in
today’s world of open standards,
combined with the inherent
interoperability that Java-based
frameworks inherit, sharing and reuse
across development teams and even
development platforms is a reality.
The second point that drives the need
to integrate WebObjects and J2EE is that both have components that are
potentially useful to developers using the other technology. For example, the
Java Naming and Directory Interface API in J2EE can be easily used in a
WebObjects application to access a diverse array of naming and directory
services, including LDAP, Novell NDS, and DNS. A J2EE-based development
effort using Enterprise JavaBeans as a middle tier for business logic could
benefit from the faster UI development and flexibility using the Direct to Java
Client technology in WebObjects.
The use of Apple’s WebObjects technologies in a J2EE-based development
project can dramatically reduce a project’s complexity, development time, and
maintainability. This is due in great part to the major benefits of the WebObjects
and EOF frameworks, including object-relational mapping and data access
abstraction (which eliminate the need to write any database access JDBC code)
and object graph management, which provides a wealth of functionality for
implementing object-based designs (including object relationship and change
management functions that would otherwise have to be coded by hand).
The following section suggests several
scenarios illustrating techniques for
integration. Finally, a set of examples with
code fragments will be presented for each
of the scenarios to demonstrate how each
scenario could be implemented. These
code examples do not necessarily
exemplify the best way to go about
integrating WebObjects and J2EE, but
they do show that it can be done.
Integration Scenarios
Scenario 1
Suppose you were developing a customer-care application using WebObjects,
and a J2EE-based framework was available containing 90 percent of the
business logic necessary to implement the required functionality. You might opt
to develop the presentation layer in WebObjects, using a combination of reusable
and custom component frameworks, and have those presentation components
access the J2EE-based business logic implemented as EJB entity and session
beans.
In this scenario, both the WebObjects
application and the J2EE container
hosting the EJBs could run on the
same server (for best performance), or
even on separate machines. The
WebObjects application locates and
invokes methods on the EJBs and
presents the data to the user via
dynamically generated HTML, just as
an EJB client such as a Servlet would.
JNDI is used to locate EJB “session
beans” messaged from the
WebObjects application, and the
communication is via RMI over IIOP.
This communication is handled by the
J2EE container provider’s (Sun,
JBoss, etc.) EJB client-side JAR files,
which are used natively by the Java
classes in the WebObjects application.
Scenario 2
Java Messaging Service (JMS) is an API for accessing enterprise messaging
systems, also known as message-oriented middleware (MOM). JMS can be
viewed as an abstraction to various vendors’ MOMs, just as JDBC abstracts
many vendors’ databases. WebObjects applications can use this API (along with
the required Java libraries from a JMS provider) to participate in an enterprise
messaging
infastructure as a
JMS client.
A good scenario to
consider is that of a
B2B system involving
several resellers and
a supplier. The
resellers all want
constant up-to-date
product availability to
ensure that their
customers receive
prompt shipments
when they order
online. The suppliers’
inventory
management system
sets up a topic and
publishes XML messages containing real-time product information. The resellers
“subscribe” to this topic, receive the messages asynchronously, decode the XML,
and update their databases. In this scenario, any of the systems based on
WebObjects (supplier systems, reseller systems, or both) could use the JMS API
to participate in the messaging.
A WebObjects application can also leverage JMS to communicate with
“message-driven beans,” a new type of EJB in the version 2.0 EJB container in
J2EE.
The following section gives code examples for each of the previous scenarios.
Integration Examples
The following examples show how to go about integrating WebObjects 5 and
J2EE similar to the scenarios that were given previously. The following examples
use and are based on the examples found in several open source projects:
• JBOSS,
a standards-compliant, Enterprise JavaBeans application
server
•
OpenJMS, an open source JMS implementation
However, any J2EE-compliant implementation of these technologies
should work. The following code examples are interesting fragments of
these open source projects.
Example 1 – WebObjects application accessing an EJB Session Bean
This example demonstrates how to access a Session Bean running in an EJB
container from a WebObjects app. As you will see, it is surprisingly trivial to
access and message EJBs from WebObjects. In this case, we will be accessing
the InterestEJB, a “stateless” session bean from the JBOSS examples. We will,
however, focus on the code it takes to access EJBs from WebObjects.
Setting up the naming environment
The first step is to set up the naming environment. A logical place is in the
constructor of Application.java, since this is a one-time setup. The second
statement passes in the host:port of the JNDI naming service, which can be
passed into the app as a command-line parameter.
//in constructor of Application.java
System.setProperty("java.naming.factory.initial","org.jnp.i
nterfaces.NamingContextFactory" );
System.setProperty("java.naming.provider.url",
nameServer);
Locating and instantiating the EJB in a wrapper class
The following code would best be encapsulated in a wrapper for the specific EJB,
then accessed from a DirectAction, WOComponent, WOSession, or
WOApplication.
The required import statements are shown below. The first two,
“javax.naming”
and javax.rmi.PortableRemoteObject, are needed to
locate and message the EJB. The third import,
“com.web_tomorrow.interest”
, is the package name of the InterestEJB’s
Home and Remote interface classes.
//in InterestBeanWrapper
import javax.naming.*;
import javax.rmi.PortableRemoteObject;
import com.web_tomorrow.interest.*;
First, we declare a state variable to hold a reference to the EJB. Since this is a
stateless session bean, we’ll use a static declaration to implement the wrapper
so that we don’t give the impression that each wrapper instance refers to a
unique bean instance.
static com.web_tomorrow.interest.Interest _interest;//a
stateless session bean
Next, we need to get a reference to the bean. This requires four basic steps.
First, get a JNDI context:
static void setEJBReference(){
InitialContext jndiContext = new InitialContext();
Next, get a general reference to the bean using its JNDI name, as specified in
the EJB deployment descriptor (see the JBOSS examples for more information):
Object ref = jndiContext.lookup("interest/Interest");
Get a reference to the bean's Home interface:
InterestHome home = (InterestHome)
PortableRemoteObject.narrow(ref, InterestHome.class);
Finally, create an Interest object from the Home interface and save it in the state
variable:
_interest = home.create();
Invoking methods on the bean
This method would be called from somewhere in the WebObjects application. We
pass the bean reference some parameters and ask it to calculate compound
interest. This is as simple as invoking a method on any ordinary Java object:
public static double calcInterest(double amt, double
rate,double per){
double d=0;
if(_interest==null)
setEJBReference();
try{
d=_interest.calculateCompoundInterest (amt,
rate, per);
}catch(Exception
ex){System.out.println(ex.toString());}
return d;
}
Accessing the EJB using the wrapper class
The final step is to do something with the bean. Now that we have a wrapper
containing a reference to the bean, we can invoke methods on it. It’s worth
mentioning that the reference is not to the bean itself, but to its “Remote
interface.” The actual bean implementation is instantiated and running in
separate Java VM process (the J2EE server’s EJB container), possibly on a
different physical server. Keep this in mind for possible performance implications.
In this example, we use the bean from a WOComponent.
//in Main.java, a WOComponent
double d= InterestBeanWrapper.calculateCompoundInterest
(1000, 0.10, 2);
System.out.println("Interest on 1000 units, at 10% per
period, compounded over 2 periods is:");
System.out.println (d);
Once the bean is deployed according to the particular J2EE server
implementation, the J2EE server and WebObjects application can now be started
and accessed.
Example 2 - WebObjects application using JMS for messaging
This example demonstrates how to have a WebObjects application participate in
an enterprise messaging system. The example is of a simple publish and
subscribe system, using the J2EE JMS API and the OpenJMS server, an open
source JMS implementation.
The sample code has a WebObjects application “publish” text messages for a
“topic.” The consumer, or “subscriber,” is a Java application that is part of the
OpenJMS examples; it subscribes to the topic and prints the message to the
console. We will focus here on the WebObjects side, the publisher in the system.
Please refer to the OpenJMS examples for the consumer example application.
The key component in this example is the JMSPublisher class. This class is a
Java object that wraps the logic necessary to publish messages to a JMS server
for a given topic, as well as maintaining the state for the connection to the JMS
server. The JMSPublisher class is typically used from a WOComponent.
The JMSPublisher class
The first step is to import the packages needed for the example:
//in JMSPublisher.java
import javax.naming.*;
import javax.jms.*;
import org.exolab.jms.client.JmsTopicConnectionFactory;
import org.exolab.jms.jndi.JndiConstants;
import
org.exolab.jms.jndi.rmi.RmiJndiInitialContextFactory;
The publisher object needs to be initialized with the host and port of the JNDI
nameserver. These can be passed in the constructor when creating the object.
The given nameserver defaults shown in the example will work on a local host
with the OpenJMS server and examples using the default constructor.
public class JMSPublisher extends Object {
int
jndiPort = 1099;
String
jndiHost = "localhost";
String mode = "rmi";
TopicConnection _topicConnection;//state variable to
hold the connection.
public JMSPublisher(String host, int port){
jndiHost=host;
jndiPort=port;
}
Setting up the TopicConnection
The topicConnection() method sets up a persistent connection in the JMS
server, and sets the connection in an instance variable. Shown here are only the
important fragments in the topicConnection() method.
TopicConnection topicConnection(){
// the communication mode to the JMS server
String modeType =
RmiJndiInitialContextFactory.class.getName();
//setup the JNDI enviornment
props.put(JndiConstants.PORT_NUMBER_PROPERTY, new
Integer(jndiPort));
props.put(JndiConstants.HOST_PROPERTY, jndiHost);
props.put(Context.INITIAL_CONTEXT_FACTORY, modeType);
//get a JNDI context
Context jndiContext = new InitialContext(props);
//get a connectionfactory reference from the context
TopicConnectionFactory factory = (TopicConnectionFactory)
jndiContext.lookup("JmsTopicConnectionFactory");
//create the connection object from the factory
_topicConnection = factory.createTopicConnection();
//create the actual connection, set into a state variable
_topicConnection.start();
The publishTopic method
The publishTopic() method wraps all the logic needed to publish a topic to
the JMS server. To publish a topic, all that’s needed is to pass in the topic name,
priority, and message. The message can be any string, including XML. The code
below is very straightforward. It should be noted that the TopicSession could also
be made persistent if several messages will be sent, so as not to cause
additional overhead creating/destroying unneeded sessions.
//JMSPublisher class:
public void publishTopic(String msg, String topicName, int
msgPriority) {
//create a topic session context
TopicSession session =
topicConnection().createTopicSession(false,
javax.jms.Session.CLIENT_ACKNOWLEDGE);
//create a topic
Topic topic = session.createTopic(topicName);
//create a publisher for the topic
TopicPublisher publisher = session.createPublisher(topic);
//set the delivery mode.
int delivery_mode = DeliveryMode.NON_PERSISTENT;
//create the message
TextMessage message = session.createTextMessage(msg);
//publish the message
publisher.publish(topic, message, delivery_mode,
msgPriority, 1000);
//close the publisher and session
publisher.close();
session.close();
Finalizer for the connection
It is a good idea to create a finalizer() method to close out the connection
when done with the object.
public void finilize(){
_topicConnection.close();
_topicConnection=null;
}
Publishing a message
The JMSPublisher class can be used in a WebObjects application to publish a
message for a given topic. All that’s needed is to instantiate a JMSPublisher
object and invoke the publishTopic() method, passing in the message, topic,
and a message priority. This can be done from a WOComponent, WOSession,
WOApplication, or DirectAction. In this example, we’re using it in a
WOComponent.
//in Main.java
public WOComponent doPublish()
{
try{
if(publisher==null)
publisher=new JMSPublisher();
publisher.publishTopic(message,topic,0);
}catch…..
This example summarizes the steps necessary to have a WebObjects application
participate in a messaging system using the JMS API. Even though this example
is based on the OpenJMS server, the steps should be essentially the same for
any JMS implementation.
Conclusion
The less code you write, the less code there is maintain, and more code reused
translates into faster development times, more stable applications, and lower
lifecycle costs. These are the primary goals everyone is trying to achieve by
using advanced technologies in the first place. The ability to seamlessly integrate
WebObjects and J2EE technologies, as demonstrated in this paper, can be an
important tool helping an organization achieve those goals.
For more information on WebObjects and WebObjects services, please visit
www.apple.com/webobjects or www.apple.com/iservices.
© 2001 Apple Computer, Inc. All rights reserved. Apple, the Apple logo, and
WebObjects are trademarks of Apple Computer, Inc., registered in the U.S. and other
countries. NextStep and OpenStep are trademarks of NeXT Software, Inc., registered in
the U.S. and other countries. Java and all Java-based trademarks and logos are trademarks
or registered trademarks of Sun Microsystems, Inc. in the U.S. and other countries. Mac
OS X Server includes software developed by the Apache Group for use in the Apache
http server project (http://www.apache.org).