OJB
Downloads
Documentation
Development
Translated (Web)
|
How to use OJB in clustered environments |
Object/Relational Bridge will work fine in environments that require robustness features such as load-balancing,
failover, and clustering. However, there are some important steps that you need to take in order for your data to be secure,
and to prevent isolation failures. These steps are outlined below.
I have tested this in a number of environments, and with Servlet engines and J2EE environments. If you run into problems,
please post questions to the OJB users mail list.
This outline for clustering is based on an email from the OJB Users Mail List:
This is that mail.
Four steps to clustering your OJB application |
A lot of people keep asking for robust ways to run their OJB engines
in multi-VM or clustered environments. This email covers how to do that
safely and securely using Open Symphony's OSCache caching product.
OSCache is a high-performance, robust caching product that supports clustering.
I've been using it for a while in production and it is excellent.
Back to the Topic: There are four main things that you should do in order
to make your OJB application ready for using a cache in a multi-VM or distributed environment.
First: Take care of the sequence manager
that you define in your repository.xml file.
The default sequence manager used in OJB is SequenceManagerHighLowImpl .
As of Release Candidate 5 (rc5), you can use SequenceManagerHighLowImpl in distributed
environments. The SequenceManagerHighLowImpl now supports its own optimistic locking
that makes the implementation cluster aware by versioning an entry in
the OJB_HL_SEQ table. However, the SequenceManagerHighLowImpl has not
been heavily tested, so if you want absolute security use an sequence manager
implementation which delegates key generation to database.
If your database supports database based sequence key generation (like PostgreSQL, Oracle, ...)
it's recommended to use SequenceManagerNextValImpl (supports database based sequence keys).
Using this sequence manager will prevent conflicts in multi-vm or clustered environments.
More about sequence manager here.
Second: If you are using SequenceManagerNextValImpl you have two possibilities:
- Let OJB define the sequence name (it's also possible to define a
sequence-name
in the field-descriptor) and auto-creating the sequence
- Do it by your own:
- Create a sequence object in your database.
- An Oracle sequence lookslike: "create sequence ackSequence increment by 1 start with 1;"
- A Postgres sequence looks like: "CREATE SEQUENCE ackSequence START 1";
- For other databases you're on your own.
- Define an
sequence-name attribute in your field-descriptor - see below
To tell OJB to use that sequence for your table add in your repository.xml the sequence
name to the field-descriptor for your table's primary key field:
 |  |  |
 |
<field-descriptor
name="ackID"
column="ACKID"
jdbc-type="INTEGER"
primarykey="true"
autoincrement="true"
sequence-name="ackSequence"
/>
|  |
 |  |  |
Third: You need to secure the data at the database.
Thomas Mahler (lead OJB developer and considerable ORM guru)
recommended in one email that you use the Optimistic Locking feature that is provided by OJB.
Sounds good to me. To do this you need to do three small steps:
- Add a database column to your table that is either an INTEGER or a TIMESTAMP
- Add the field to your java class, and getter/setter methods:
 |  |  |
 |
private Integer ackOptLock;
public Integer getAckOptLock()
{
return ackOptLock;
}
public void setAckOptLock(Integer ackOptLock)
{
this.ackOptLock = ackOptLock;
}
|  |
 |  |  |
- Add the column to your table in your repository:
 |  |  |
 |
<field-descriptor
name="ackOptLock"
column="ACKOPTLOCK"
jdbc-type="INTEGER"
locking="true"/>
|  |
 |  |  |
Now OJB will handle the locking for you. No explicit transactional code necessary!
Fourth: Do The Cache. You're basically in good shape at this point.
Now you've just got to set up OSCache to work with OJB. Here are the steps for that:
-
Download OSCache from OSCache. Add the oscache-2.0.x.jar to
your project so that it is in your classpath (for Servlet/J2EE users place in your WEB-INF/lib directory).
You will also need commons-collections.jar and commons-logging.jar, if you don't already have them.
-
Download JavaGroups from JavaGroups. Add the
javagroups-all.jar to your classpath (for Servlet/J2EE users place in your WEB-INF/lib directory).
-
In your OJB.properties file change the ObjectCacheClass property to be the following:
 |  |  |
 |
ObjectCacheClass=org.nacse.jlib.ObjectCacheOSCacheImpl
|  |
 |  |  |
To make OSCache the default used cache implementation. More info
about object caching here.
-
Add oscache.properties from your OSCache distribution to your project so that it is in the classpath
(for Servlet/J2EE users place in your WEB-INF/classes directory). Open the file and make the following changes:
-
Add the following line to the CACHE LISTENERS section of your oscache.properties file:
 |  |  |
 |
cache.event.listeners=com.opensymphony.oscache.plugins.
clustersupport.JavaGroupsBroadcastingListener
|  |
 |  |  |
-
Add the following line at the end of the oscache.properties file (your network must support multicast):
 |  |  |
 |
cache.cluster.multicast.ip=231.12.21.132
|  |
 |  |  |
- Add the following class to your project (feel free to change package name, but make sure
that you specify the full qualified class name in OJB.properties file).
You can find source of this class under
db-ojb/contrib/src/ObjectCacheOSCacheImpl .
Source for ObjectCacheOSCacheImpl :
 |  |  |
 |
import org.apache.ojb.broker.Identity;
import org.apache.ojb.broker.PersistenceBroker;
import org.apache.ojb.broker.cache.ObjectCache;
import org.apache.ojb.broker.cache.RuntimeCacheException;
import com.opensymphony.oscache.base.CacheEntry;
import com.opensymphony.oscache.general.GeneralCacheAdministrator;
public class ObjectCacheOSCacheImpl implements ObjectCache {
private static GeneralCacheAdministrator admin = new GeneralCacheAdministrator();
private static final int NO_REFRESH = CacheEntry.INDEFINITE_EXPIRY;
public ObjectCacheOSCacheImpl() {
}
public ObjectCacheOSCacheImpl(PersistenceBroker broker, Properties prop) {
}
public void cache(Identity oid, Object obj) {
try {
this.remove(oid);
admin.putInCache(oid.toString(), obj);
}
catch (Exception e) {
throw new RuntimeCacheException(e.getMessage());
}
}
public Object lookup(Identity oid) {
try {
return admin.getFromCache(oid.toString(), NO_REFRESH);
}
catch (Exception e) {
admin.cancelUpdate(oid.toString());
return null;
}
}
public void remove(Identity oid) {
try {
admin.flushEntry(oid.toString());
}
catch (Exception e) {
throw new RuntimeCacheException(e.getMessage());
}
}
public void clear() {
if (admin != null) {
try {
admin.flushAll();
}
catch (Exception e) {
throw new RuntimeCacheException(e);
}
}
}
}
|  |
 |  |  |
You're ready to go! Now just create two instances of your application and see how they communicate at the cache level. Very cool.
Notes:
- For J2EE/Servlet users: I have tested this on a number of different application servers. If you're having
problems with your engine, post an email to the OJB Users mail list.
- OSCache also supports JMS for clustering here, which I haven't covered. If you either don't have access to
a multicast network, or just plain like JMS, please refer to the OSCache documentation for help with that
(OSCache Clustering with JMS).
- I have also tested this with Tangosol Coherence. Please refer to this Blog entry for that setup:
Coherence Setup
- OJB also has ships with JCS. Feel free to try that one out on your own.
|
|
|