3

Currently we have a deployment architecture were a bunch of data oriented services are exposed via RMI to business services. Both types (the data oriented services and the business services) are stateless session beans. Each data-service interface package (containing the remote interfaces) also has a locator, that does the JNDI lookup. We so this so that we can call an data oriented service from anywhere in the business service logic.

This is how a locator looks like:

public final class OMRLocator {

    private static final Logger LOG = Logger.getLogger( OMRLocator.class );

    private static final String ORG_WILDFLY_INITIAL_CTX_FACTORY = "org.wildfly.naming.client.WildFlyInitialContextFactory";

    private OMRLocator() {
    }

    @Produces
    public static OrganisationsAndMandatesRegister locate() {
        try {
            Properties ctxProp = new Properties();
            ctxProp.put( Context.INITIAL_CONTEXT_FACTORY, ORG_WILDFLY_INITIAL_CTX_FACTORY );
            InitialContext ctx = new InitialContext( ctxProp );
            return (OrganisationsAndMandatesRegister) ctx.lookup( OrganisationsAndMandatesConstants.REMOTE_NAME );
        }
        catch ( NamingException ex ) {
            LOG.log( Level.WARN, "Cannot reach: " + OrganisationsAndMandatesConstants.REMOTE_NAME, ex );
            return null;
        }
    }
}

We were running on JBOSS EAP6 and started to experiment with CDI. Hence, we added a beans.xml to the data-service-beans and the @Produces to make the (in this case OrganisationAndMandatesRegister CDI injectable. The idea is that the future we might repackage our application and package the data-services together with the business-service in one enterprise archive.

Lately we migrated to JBOSS EAP7.2 (Wildfly 8?) and suddenly we see all kinds of unexpected latency and transaction problems.

My suspicion is that the way we obtain beans are a factor in these problems. For instance: I guess the scope is dependent on the business EJB lifecycle, but for each call to the locate() in the business service a new instance of a data-service is produced.

So: what is the best way to produce a remote bean (via RMI) when using CDI? Should I take scoping into consideration given that both types of services are stateless (or is this done automatically)?

Sjaak
  • 3,602
  • 17
  • 29

1 Answers1

4

If no scope is defined on the producer method then @Dependend is used, so find the proper scope, maybe @RequestScoped. When you retrieve an EJB from JNDI you do not get a new instace, you get an instance from the pool which could be the same over multiple calls. Your problem could be the EJB interceptors because if dependent scoped the EJB instance is always the same once injected an is never released.

Get rid of the @Produces because CDI integrates with EJB and EJBs can be injected via @Inject or @EJB. If you want to keep the Locator class then you can inject the EJBs in there and return the proper EJB instance (which is actually a proxy), whereby the Locator should be @ApplicationScoped. Another way is to to use Instance which alows a programmatic lookup. With the type Object you can access all CDI Beans (including EJBs) of the container so a common interface would be useful to minimize accessible beans.

See the following links for more help.

https://docs.jboss.org/weld/reference/latest/en-US/html/injection.html#_obtaining_a_contextual_instance_by_programmatic_lookup

Inject a stateless EJB with @Inject into CDI Weld ManagedBean (JSF 1.2 EJB Application on jboss 6 AS)

http://www.adam-bien.com/roller/abien/entry/inject_vs_ejb


Just to summarise:

option a) Leave as is. Perhaps make scope explicit with @Dependent to indicate this is called at calling-bean-creation (injecting in constructor of calling bean)

option b) Use a stateless @ApplicationScoped session bean

@LocalBean // otherwise @EJB will not work
@ApplicationScoped // this instance should be created only once per ear
public class OMRLocator {

    @EJB // does implicitly a remote (default) JNDI lookup
    private OrganisationsAndMandatesRegister instance;

    @Produces
    @Dependent // just to make it explicit
    public OrganisationsAndMandatesRegister locate() {
       return instance;
    }
}
Sjaak
  • 3,602
  • 17
  • 29
Thomas Herzog
  • 506
  • 2
  • 6
  • Thanks for the elaborate answer. As soon as I have a possibility to try it in our (true) remote setup I'll get back to this. – Sjaak May 11 '19 at 09:31
  • Hertzog, I don't want to introduce more beans. However, I experimented a bit with the `@Produces`. What surprises me (when I debug) is that plain `@Produces` leads to the `locate` method only be called once. However, when I add `@RequestScoped` to `@Produces` `locate` is suddenly called each bean invocation (what I would have expected in the first place since scope is dependent ). I don't understand this yet. – Sjaak May 16 '19 at 21:17
  • 1
    DependendScoped Beans are only produced once for injection per bean instance which injects it. They are like ordinary instances, no proxy manages them, they are owned and managed by the bean which got it injected. RequestScoped beans are produced and injected for each request. So locate should be called once per produced EJB per request. – Thomas Herzog May 17 '19 at 04:18
  • Ah. The penny starts to drop. I now understand that 1) if you use CDI scope annotation together with `@Produces`, it is completely independent of the lifecycle of the stateless bean constructor injection. 2) if you don't use CDI scope annotation in conjunction with `@Produces` (==default==`@Dependent`) it follows the life cycle of the stateless session bean, not of the request.... You mentioned that the locator should be @ApplicationScoped this means the relation between calling EJB and remote EJB is there for the application life, never refreshed. Isn't that risky? – Sjaak May 18 '19 at 07:09
  • Each time the producer is called an instance is created of the class containing the producer with dependend scope. If application scoped the instance is created only once. If you leave the jndi lookup, the should not be a problem. If you inject the EJBs via EJB annotation and return the reference, then it should not be problem because you return a proxy which acceses the acutual bean of the ejb container. Maybe you try and debug. – Thomas Herzog May 18 '19 at 15:38
  • Ok. So if i understand you correctly there's a difference between @EJB and a plain JNDI lookup with `@Produces` as above?. (proxy vs no proxy)....... The beans (I have several) were the `@Produces` actually inject, have constructor injection which I prefer over field (unit test). AFAIK, @EJB can only be used on fields. I tried to remove the locator all together, but I can't use @Inject for remote EJBs it seems (Weld complains).. I think @EJB works only on enterprise beans, so the locator itself must be an `@ApplicationScoped` (local) stateless bean if I follow your advice. – Sjaak May 19 '19 at 08:40
  • And I did of course do some debugging. But there are too many variables sometimes to make sense of what you see :). The on-line documentation on CDI and EJB is not always concise on exactly these topic. Add to this a new (version) of the Application Server and its easy to get lost. This is very helpful for me. – Sjaak May 19 '19 at 08:45