By deploying Kodo into a J2EE environment, you can maintain the simplicity and performance of Kodo, while leveraging J2EE technologies such as container managed transactions (JTA/JTS), enterprise objects with remote invocation (EJB), and managed deployment of multi-tiered applications via an application server. This tutorial will demonstrate how to deploy Kodo-based J2EE applications and showcase some basic enterprise JDO design techniques. The tutorial's sample application attempts to model a basic garage catalog system. While the application is relatively trivial, the code has been constructed to illustrate simple patterns and solutions to common problems when using Kodo in an enterprise environment.
This tutorial assumes that you have installed Kodo and setup your classpath according to the installation instructions appropriate for your platform. In addition, this tutorial requires that you have installed and configured a J2EE-compliant application server, such as JBoss, WebSphere, WebLogic, Borland Enterprise Server, JRun, or SunONE / Sun JES. If you use a different application server, this tutorial may be adaptable to your application server with small changes; refer to your application server's documentation for any specific classpath and deployment descriptor requirements.
This tutorial assumes a reasonable level of experience with Kodo and JDO. We provide a number of other tutorials for basic Kodo and JDO concepts, including enhancement, schema mapping, and configuration. This tutorial also assumes a basic level of experience with J2EE components, including EJB, JNDI, JSP, and EAR/WAR/JAR packaging. Sun and/or your application server company may provide tutorials to get familiar with these components.
In addition, this tutorial uses Ant to build the deployment archives. While this is the preferred way of building a deployment of the tutorial, one can easily build the appropriate JAR, WAR, and EAR files by hand, although that is outside the scope of this document.
Every application server has a different installation process for installing J2EE components. Kodo can be installed in a number of ways, each of which may or may not be appropriate to your application server. While this document focuses mainly upon using Kodo as a JCA resource, there are other ways to use Kodo in a J2EE environment.
JCA
: Kodo implements the
JCA 1.0 spec, and the kodo-jdo.rar file that comes in the
jca
directory of the distribution can be
installed as any other JCA connection resource. This is the
preferred way to integrate Kodo into a J2EE
environment. It allows for simple installation
(usually involving uploading or copying kodo-jdo.rar
into the application server), guided configuration
on many appservers, as well as dynamic reloading
for upgrading Kodo to a newer version. The drawback
is that older application servers have flaky or non-
existent JCA support as the spec is relatively new.
PersistenceManagerFactoryImpl
: This
remains the most compatible way to integrate Kodo
into a J2EE environment, though this is not seamless
and can require a fair bit of custom application server
code to manually bind an instance of
PersistenceManagerFactoryImpl
into the
JNDI tree. This is somewhat offset by avoiding JCA
configuration issues as well as being able to tailor the
binding and start-up process.
Section 8.1.4, “Kodo JDO JCA Deployment” goes over the steps to install Kodo on your application server of choice via the Java Connector Architecture (JCA). If your application server does not support JCA, see Section 8.1.5, “Non-JCA Application Server Deployment” and Section 8.1.1, “Standalone Deployment” for other installation options.
Installing the sample application involves first compiling and building a deployment archive (.ear) file. This file then needs to be deployed into your application server.
Navigate to the samples/jdo/j2ee
directory
of your Kodo installation. Compile the source files in place
both in this base directory as well as the nested
ejb
directory:
javac *.java ejb/*.java
Enhance the Car class.
kodoc -p jdo.properties package.jdo
Run the mapping tool; make sure that your
META-INF/jdo.properties
file includes the
same connection information (e.g. Driver, URL, etc.) as your
installation:
mappingtool -p jdo.properties package.jdo
Configure options in samples.properties
to match your JCA installation, most notably the JNDI
name to which you have bound Kodo (it defaults to kodo).
Warning | |
---|---|
This step (editing
Be sure that the setting you put for
JBoss, for example, will prefix
the JNDI name with |
Build an J2EE application archive by running Ant against the
build.xml
. This will create
samplej2ee.ear
. This ear can now be
deployed to your appserver.
ant -f build.xml
Place the ear file in the deploy
directory of your JBoss installation. You can use the
above hints to view the JNDI tree to see if
samples.jdo.j2ee.ejb.CarHome
was
deployed to JNDI.
Place the ear file in the applications
directory of your WebLogic domain. Production mode (see your
startWebLogic.sh/cmd file) should be set to false to enable
auto-deployment. If the application was installed correctly,
you should see sample-ejb
listed
in the Deployments/EJB section of the admin console.
In addition you should find CarHome
listed in
the JNDI tree under
myserver->samples->j2ee->ejb
.
Create a new directory named samplej2ee.ear
in the applications
directory of
your WebLogic domain. Extract the EAR file (without copying
the EAR file) to this new directory:
applications> mkdir samplej2ee.ear applications> cd samplej2ee.ear samplej2ee.ear> jar -xvf /path/to/samplej2ee.ear
Deploy the application by using the admin console
(Deployments -> Applications -> Deploy a new Application.
Select samplej2ee.ear
and deploy
to the proper server targets. If you have installed the
application correctly, you should find CarHome
listed in the JNDI tree under
myserver->samples->j2ee->ejb
.
Browse to the admin console in your web browser. Select
Applications/ Enterprise Applications
from the left navigation panel. Select
Deploy...
and in the
following screen upload the samplej2ee.ear
file to the server. Apply your changes by selecting the
link in the upper right portion of the page. You should
now see samplej2ee
listed
in the Enterprise Applications folder of the navigation panel.
Browse to the admin console in your web browser. Select
J2EE Components
from the left navigation panel. Select
Add
under the
Enterprise Applications
heading. Select the samplej2ee.ear
file and hit Deploy
.
You should now see Sample-KodoJ2EE
listed in the top-level folder of the navigation panel.
Select it, and then select the
sample-ejb.jar#CarEJB
component under
Enterprise JavaBeans
section, then change the JNDI Name
for the bean from the default value to
samples.jdo.j2ee.ejb.CarHome
and
hit Apply
.
If the Kodo resource adapter and the sample EAR are both
configured correctly, you should now be able to access
your sample application at JRun's deploy URL (e.g.,
http://localhost:8100/sample/
).
Browse to the admin console in your web browser. Select
Applications / Install New Application
from the left navigation panel. Select
the path to your samplej2ee.ear
file
and press Next
.
On the following screen, leave the options at the default
and select Next
. On the following
screen (Install New Application->Step 1
), ensure that the Deploy EJBs
option is checked. Leave other options at their
defaults.
Move on to Step 2. On this screen enter
samples.jdo.j2ee.ejb.CarHome
as the JNDI name for the
Car
EJB. Continue through the
remaining steps leaving options at the defaults. Select
Finish
and ensure that
the application is deployed correctly.
Save the changes to the domain configuration by either
selecting the Save
link
that appears after the installation is complete or by
selecting Save
from the top menu.
To verify your installation, select Applications
/ Enterprise Applications
from the left
navigation panel. Sample-KodoJ2EE
should be listed in the list. If the application has not
started already, select the checkbox next to
Sample-KodoJ2EE
and select Start
.
Deploy the EAR file using iastool or the console. Note
that you may have to include the JDO library in your
stub generation process. Also be sure that you have
followed the JCA instructions for
BES as well as editing the samples.properties to
point to serial://kodo
JNDI location.
You should be able to see the CarEJB located in JNDI
located at the home classname.
The sample application installs itself into the web layer at the
context root of sample. By browsing to
http://yourserver:yourport/sample
, you should be presented with a simple
list page with no cars. You can edit, add, delete car instances.
In addition, you can query on the underlying Car
PersistenceCapable
instance by
passing in a JDOQL query into the marked form (such as
model=="Some Model"
).
The garage application is a simple enterprise application that demonstrates some of the basic concepts necessary when using Kodo in the enterprise layer.
The core model wraps a stateless session bean facade around a persistence capable instance. Using a session bean provides both a remote interface for various clients as well as providing a transactional context in which to work (and thus avoiding any explicit transactional code).
This session bean uses the Data Transfer Object pattern
to provide the primary communication between the
application server and the (remote) client. The Car
instance will be used as the primary object upon
which the client will work.
This model can be easily adapted to using entity beans.
See our sample ejb
directory and
JDOEntityBean
for more details on how to using JDO to power your entity beans.
samples/jdo/j2ee/Car.java
:
The core of the sample application. This is the
persistence capable class that Kodo will use to persist the
application data.
Instances of this class will also fill the role of
data transfer object (DTO) for EJB clients.
To accomplish this, Car
implements
java.io.Serializable
so that remote clients can access cars as parameters
and return values from the EJB.
samples/jdo/j2ee/package.jdo
:
The JDO metadata file for the package.
Note that some appservers have a problem with the
way we include an internal DTD. If you see a lot
of illegal/malformed character exceptions, uncomment
out the DTD declaration in this file.
samples/jdo/j2ee/SampleUtilities.java
:
This is a simple facade to aggregate some core
JDO functionality into some static methods.
By placing all of the functionality into a single
facade class, we can reduce code maintenance, as
well as having the added advantage of being able
to access Kodo from other portions of the
application such as a servlet or JSP (though this
functionality is not demonstrated in this sample).
In addition, some simple utility functions such as
JNDI helper methods are also in this class.
samples/jdo/j2ee/ejb/Car*.java
:
The source for the CarEJB
session bean.
Clients can use the CarHome
and CarRemote
interfaces to
find, manipulate, and persist changes to
Car
transfer object
instances. By using J2EE transactional features,
the implementation code in CarBean.java
can be focused almost entirely upon business and
persistence logic without worrying about transactions.
samples/jdo/j2ee/jsp/*.jsp
:
The web presentation client. These JSPs are not aware of JDO;
they simply use the CarEJB
session
bean and the Car
transfer object to do
all the work.
samples/jdo/j2ee/resources/*
:
Files required to deploy to the various appservers,
including J2EE deployment descriptors, WAR/ EJB/ EAR
descriptors, as well as appserver specific files.
samples/jdo/j2ee/build.xml
:
A simple Ant build file to help in creating a J2EE EAR file.
samples/jdo/j2ee/samples.properties
:
A simple .properties
file to tailor the
sample application to your particular appserver and
installation.
Persistence capable instances are excellent
candidates for the Data Transfer Object Pattern.
This pattern attempts to reduce network load, as well
as group business logic into concise units.
For example, CarBean.edit
allows you
to ensure that all values are correct before committing a
transaction, instead of sequentially calling getters and
setters on the session facade. This is especially true when
using RMI such as from a Swing based application
connecting to an application server.
CarEJB
works as a session bean facade
to demarcate transactions, provide finder methods, and
encapsulate complex business logic at the server level.
Persistent instances using datastore identity
lose all identity upon serialization (which happens
when they are returned from an EJB method). While
there are numerous ways to solve this problem, the
sample uses the clientId
field of
Car
to store the stringified
datastore id. Note that this field is
not persistent. This field ensures
that the client and EJB always share the same
identity for a given car instance.
Note | |
---|---|
Note that this situation is slightly different for application identity. While persistent instances under application-identity still lose their id instance, recreating it from the server side is trivial as the fields on which it is based are still available. |
Other ways of solving this problem include:
Transmitting the object id first or otherwise storing identity with the client:
Object oid = dogRemote.findByQuery ("// some query"); Dog dog = dogRemote.getDogForOid (oid); // changes happen to dog dogRemote.save (oid, dog);
Wrapper objects that store the persistent instance, object id instance, as well as potentially other application-specific data:
public class DogTransferObject { private Dog dog; private Object oid; private String authentication; // some application specific client data }
PersistenceManager.close
should be called at the end of every EJB method.
In addition to ensuring that your code will not attempt to
access a closed PersistenceManager
, it
allows the JDO implementation to free up unused resources.
Since a PersistenceManager
is created
for every transaction, this can increase the scalability
of your application.
You should not use
PersistenceManager.currentTransaction
or any
javax.jdo.Transaction
methods.
Instead, use the JTA transactional API.
SampleUtilities
includes a helper method
to obtain a UserTransaction
instance
from JNDI (see your application server's documentation on
the proper JNDI lookup).
While serialization of PC instances is relatively straightforward, there are several things to keep in mind:
Collections and iterators returned from
Query
instances become closed and inaccessible upon method
close. If the client is receiving the results of a
Query
, such as in
CarBean.query
, the results
should be transferred to
another fresh collection instance before being returned
from the server.
While "default fetch group" values will always be
returned to the client upon serialization, lazily
loaded fields will not as the
PersistenceManager
will
have been closed before those fields attempt to
serialize. One can either simply access those fields
before serialization, or one can use the persistence
mangaer's retrieve
and
retrieveAll
methods to make
sure object state is loaded. Note that these methods
are not recursive. If you need to go through multiple
relations, you must call retrieve
at each relational depth. This is an
intentional limitation in the specification to prevent
the entire object graph from being serialized and/or
retrieved.
It is not necessarily required that you use EJBs and
container-managed transactions to demarcate transactions,
although that is probably the most common method.
In EJBs using bean managed transactions, you can control
transactions through either the javax.jdo.Transaction
or the
javax.transaction.UserTransaction
.
Furthermore, outside of EJBs you can access the JDO layer with
either transactional API.
The Kodo distribution includes source code for some convenient
base classes that encapsulate much of the code that is
laid out in this example for clarity.
JDOBean
, JDOSessionBean
,
and JDOEntityBean
include most of the
functionality of SampleUtilities
, and
handle common EJB interface methods such as
setEntityContext
. To use these
classes, we recommend placing Kodo's jars into the system
classpath and not into the ear. Ear deployment can cause
classloader problems due to the multiple locations that these
classes could be loaded from.
PersistenceManager
s are allocated on a
per-Transaction basis.
Calling getPersistenceManager
from
the same PersistenceManagerFactory
within the same EJB method call will always return the same
instance.
SamplesUtilities.getPersistenceManager () == SampleUtilities.getPersistenceManager (); // will always be true