Java – Caching remote EJB 3.0 reference

cachingejb-3.0java

I was thinking how could i save time on looking up remote ejb reference through jndi. I had an application that needed to work very fast, but it also had to call remote ejb which slowed it down.

So my solution was something like this:
I took apache commons-pool library and used its StackObjectPool implementation for my remote ejb references cache.

private static final ObjectPool pool = new StackObjectPool(new RemoteEjbFactory());

Factory looks something like this:

public static class RemoteEjbFactory extends BasePoolableObjectFactory {

    @Override
    public Object makeObject() {
        try {
            return ServiceLocator.lookup(jndi);
        } catch (NamingException e) {
            throw new ConfigurationException("Could not find remote ejb by given name", e);
        }
    }
}

Then i take object by borrowing it from pool (if no free object in pool it uses factory to create one):

SomeEjbRemote someEjb = null;
try {
        someEjb = (SomeEjbRemoteImpl) pool.borrowObject();
        someEjb.invokeRemoteMethod();
} catch (Throwable t) {
        if (someEjb != null) {
            pool.invalidateObject(someEjb);
        }
        pool.clear(); // Maybe its not neccessary
        someEjb = (SomeEjbRemoteImpl) pool.borrowObject();
        someEjb.invokeRemoteMethod(); // this time it should work
}

And of course returning ejb back to pool after successful invokacion

finally {
    try {
         pool.returnObject(someEjb);
    } catch (Exception e) {
        logger.error("Could not return object to pool.", e);
    }
}

As i understand there is no guarantee that remote reference will stay connected so if we catch exception using cached remote ejb, we just invalidate that object and retry.

What do you think about such approach? Is it correct? Maybe some other solutions, advices?

Best Solution

From the spec

3.4.9 Concurrent Access to Session Bean References

It is permissable to acquire a session bean reference and attempt to invoke the same reference object concurrently from multiple threads. However, the resulting client behavior on each thread depends on the concurrency semantics of the target bean. See Section 4.3.14 and Section 4.8.5 for details of the concurrency behavior for session beans.

Summary of § 4.3.14:

If the bean is SLSB, each call will be served by one EJB in the app. server pool. The app. server synchronizes the calls the EJB instances, so each EJB instance are never accessed concurrently.

For SFSB, each call is dispatch to one specific EJB instance, and the app. server does not synchronises the call. So two concurrent calls to the remote reference might lead to a concurrent access to the EJB instance which raises then a javax.ejb.ConcurrentAccessException. The client is responsible of the correct synchronization of the access to the remote reference.

And § 4.8.5 is about EJB singleton, probably not what you are using.

I assume you use SLSB, so you don't need to have a pool on the client-side: look up the remote bean once, and use the same reference from multiple threads.

You could however do a benchmark to see if using multiple reference improves performance, but the gain -- if any -- is probably neglectable compare to the cost of the remote invocation itself.

If then you still decide to have more than one remote reference, I would suggest an other design. Based on your question I assume you have a multi-threaded app. You probable already use a pool for the threads, so a pool for the reference is maybe redundant. If each thread gets a remote reference when it is created, and threads are pooled, there won't be that many remote lookup and the design is simplified.

My 2 cents

Related Question