users@glassfish.java.net

Re: EJB3 Remote Referece Thread Safe?

From: Witold Szczerba <pljosh.mail_at_gmail.com>
Date: Tue, 9 Dec 2008 14:40:48 +0100

I told you how to work around this issue. I did it even before I
noticed that bug, so I know about it from this thread. OK, I will
attach complete solution. This is one interface:
public interface EjbLookup {
    String defaultBeanName(Class remoteInterface);
    <T> T getBean(Class<T> remoteInterface) throws NamingException;
    <T> T getBean(Class<T> remoteInterface, String beanName) throws
NamingException;
}
implemented by SimpleEjbLookup. The EjbLookup is used by
EjbLookupRetryProvider which has two public constructors:

EjbLookupRetryProvider(Provider<EjbLookup> ejbLookupProvider,
Class<T>remoteInterface, String beanName)
and
EjbLookupRetryProvider(Provider<EjbLookup> ejbLookupProvider,
Class<T>remoteInterface)

When using second constructor, the default bean name is acquired using
provided EjbLookup#defaultBeanName method. EjbRetryLookupProvider is a
factory which use EjbLookups to obtain remote bean and wraps it using
standard Java reflection library to something we can call: unbreakable
remote stubs :) These objects looks like raw remote beans, but this is
actually just a decorator/aspect over the real stubs obtained from
Context. When you call a method:
someRemoteBean.doSomething()
and the underlying stub will throw InvocationTargetException because
of terrible Glassfish implementation,
that exception is intercepted, the Context once again asked for that
bean and the call is retried. When sucessfull, you will even not
notice something went wrong, as a user of such a bean - you will just
get the result as nothing had happened!
The number of retries can be changes, I set it to 2.
There is one more advantage: when you restart server - every single
remote session bean stub will not be able to work unless you ask for
new instances from initial context. When using my
EjbLookupRetryProvider, your remote beans will always survive as they
will silently, without you noticing it will do that by themselves
during first call to the method after the server was restarted.

This is the essential code which is able to look the bean up when
something goes wrong:
this method is part of EjbLookupRetryProvider:

private static final Object ERROR = new Object();
private final int maxTries = 2;
private final Provider<EjbLookup> ejbLookupProvider;
private final Class<T> remoteInterface;
private final String beanName;
private Object bean = null;

@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
  Object result = ERROR;
  Throwable throwable = null;
  for (int tryCnt = 1; tryCnt <= maxTries; tryCnt++) {
      logger.info(String.format(
              "try %d: Intercepting method %s, args:%s",
              tryCnt, method, Arrays.toString(args)));
      try {
          if (bean == null) {
              bean =
ejbLookupProvider.create().getBean(remoteInterface, beanName);
          }
          result = method.invoke(bean, args);
      } catch (InvocationTargetException e) {
          throwable = handleException(e, tryCnt);
          bean = null;
      }
      if (result != ERROR) {
          return result;
      }
  }
    throw throwable;
}
private Throwable handleException(InvocationTargetException e, int tryCnt) {
  Throwable result = e.getCause();
  if (result instanceof NoSuchEJBException) {
      logger.warning(String.format(
              "Got NoSuchEJBException when calling: %s. tryCnt=%d",
              beanName, tryCnt));
  }
  return e.getCause();
}

All three classes are included as attachment. They will set you free
from that bug.
Here is example usage from real application bootstrap class:

public class SopBootstrap ... {
private final ServerConnectionModel serverConnectionModel;

    private <T> T ejbLookup(Class<T> remoteInterface) {
        return new EjbLookupRetryProvider<T>(serverConnectionModel,
remoteInterface)
                .getBean();
    }

    public SopBootstrap(...) {
        serverConnectionModel = new ServerConnectionModel(
                serverAddress,
                new ProgrammaticLogin());

        mainFrame = new MainFrame(
                new
BuildDateInfo(ejbLookup(SystemPropertiesServiceRemote.class)),
                serverConnectionModel);
    }
}

In the above example you can see the creation of ServerConnectionModel
which does many things for application, but the most important thing
here is that it knows how to create EjbLookups objects as it is the
one who takes care of Context. ServerConnectionModel implements
Provider<EjbLookup> - which has only one method: "create" which
returns EjbLookup objects.

The MainFrame class is the main GUI class - as you can see this class
needs BuildDateInfo object - that objects, however, needs object which
implements SystemPropertiesServiceRemote. So, ejbLookup method is
invoked, it creates new EjbLookupRetryProvider passing remote
interface name and returns the object which I was talking about - it
looks like real remote stub but it will not fail because of
InvocationTargetException. Of course BuildDateInfo does not care if
this is real stub created by initial context or anything else - as
long as it implements required methods.

Regards,
Witold Szczerba

2008/11/17 <glassfish_at_javadesktop.org>:
> It's hard to reproduce this problem so providing a failing test is tricky at best. We're still having this issue regardless of the GF2 build we use. We create one remote reference and want to use it as long as the app runs. Given the performance constraints we have doing the lookup each call isn't a solution, nor is waiting for it to go wrong and then do another lookup (we only notice after 30 minutes, by default).
>
> What is rather worrying though is that this issue is supposed to be fixed as of 2.0b49, although the issue resolution described in that report is rather vague at best.
>
> Given that this is a rather important problem with what should be a stable J2EE platform I would think this issue would receive a bit more attention than it has been getting. We, and I'm sure others like us, now have to seriously consider migrating to another app server. As we are very happy with GF as a whole this certainly isn't the preferred solution.
> [Message sent by forum member 'remonvv' (remonvv)]
>
> http://forums.java.net/jive/thread.jspa?messageID=317078
>