Payara icon indicating copy to clipboard operation
Payara copied to clipboard

Bug Report: EJBs can't be initialised/found when injected into and Interceptor in an EAR/FISH-5888

Open CarlosMOGoncalves opened this issue 3 years ago • 4 comments

Description


Hello there,

I have found an issue when injecting EJBs into an Interceptor that occurs when we are in an EAR project. In sum, the EJB cannot be found via annotation BUT can be found if we do a manual lookup.

This happens only in EAR projects, even if the EJB is in the same module as the Interceptor. It works fine when packaged into a single WAR, for example on Payara Micro or a single WAR deployed into the Server.

This behaviour was found in Payara Server 5.2020.6 but I tested it with 5.2021.6 and it remains the same

Expected Outcome

Injected EJB into an Interceptor is found and initialised normally.

Current Outcome

When an injected EJB is a Stateless bean an EJBException (that comes from a NameNotFoundException) is thrown that reads:

2021-08-26T17:49:27.759+0100|WARNING: javax.ejb.EJBException: javax.ejb.EJBException: javax.ejb.CreateException: Could not create stateless EJB
	at com.sun.ejb.containers.StatelessSessionContainer._getContext(StatelessSessionContainer.java:399)
	at com.sun.ejb.containers.BaseContainer.getContext(BaseContainer.java:2598)
	at com.sun.ejb.containers.BaseContainer.preInvoke(BaseContainer.java:2019)
	at com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHandler.java:210)
	at com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate.invoke(EJBLocalObjectInvocationHandlerDelegate.java:90)
       ...(ommited)
       ...(ommited)
Caused by: javax.naming.NameNotFoundException: No object bound to name java:comp/env/pt.test.ejbs.interceptors.FunctionLoggerInterceptor/dataCache
	at com.sun.enterprise.naming.impl.GlassfishNamingManagerImpl.lookup(GlassfishNamingManagerImpl.java:765)
	at com.sun.enterprise.naming.impl.GlassfishNamingManagerImpl.lookup(GlassfishNamingManagerImpl.java:738)
	at com.sun.enterprise.naming.impl.JavaURLContext.lookup(JavaURLContext.java:159)
	at com.sun.enterprise.naming.impl.SerialContext.lookup(SerialContext.java:476)

When the EJB is a Singleton an EJBException (that comes from a NameNotFoundException) is thrown:

2021-08-26T17:46:02.230+0100|WARNING: javax.ejb.EJBException: javax.ejb.CreateException: Initialization failed for Singleton PeriodicService
	at com.sun.ejb.containers.AbstractSingletonContainer$SingletonContextFactory.create(AbstractSingletonContainer.java:689)
	at com.sun.ejb.containers.AbstractSingletonContainer.instantiateSingletonInstance(AbstractSingletonContainer.java:421)
	at org.glassfish.ejb.startup.SingletonLifeCycleManager.initializeSingleton(SingletonLifeCycleManager.java:219)
	at org.glassfish.ejb.startup.SingletonLifeCycleManager.initializeSingleton(SingletonLifeCycleManager.java:180)
	at com.sun.ejb.containers.AbstractSingletonContainer.checkInit(AbstractSingletonContainer.java:395)
	at com.sun.ejb.containers.AbstractSingletonContainer._getContext(AbstractSingletonContainer.java:185)
	at com.sun.ejb.containers.CMCSingletonContainer._getContext(CMCSingletonContainer.java:120)
	at com.sun.ejb.containers.BaseContainer.getContext(BaseContainer.java:2598)
	at com.sun.ejb.containers.BaseContainer.preInvoke(BaseContainer.java:2019)
	at com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHandler.java:210)
	at com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate.invoke(EJBLocalObjectInvocationHandlerDelegate.java:90)
        ...(ommited)
        ...(ommited)
Caused by: javax.naming.NameNotFoundException: No object bound to name java:comp/env/pt.test.ejbs.interceptors.FunctionLoggerInterceptor/dataCache
	at com.sun.enterprise.naming.impl.GlassfishNamingManagerImpl.lookup(GlassfishNamingManagerImpl.java:765)
	at com.sun.enterprise.naming.impl.GlassfishNamingManagerImpl.lookup(GlassfishNamingManagerImpl.java:738)
	at com.sun.enterprise.naming.impl.JavaURLContext.lookup(JavaURLContext.java:159)
	at com.sun.enterprise.naming.impl.SerialContext.lookup(SerialContext.java:476)
	... 123 more

Steps to reproduce

First, it must be an EAR project.

Then, all it is needed is a simple Interceptor that has an EJB injected into it and then somehow try to initialise that interceptor, for example invoking any bean that is intercepted by it from an endpoint. The server won't be able to initialise it and will throw one of the above exceptions.

You can always use these two projects here: server ear and this micro ear.

The first one is a simple app packaged as an EAR meant to be run in Payara Server.

  1. It has an endpoint in webservices module on a JAX-RS resources called InterceptorResource
  2. That endpoint calls an EJB called PeriodicService
  3. That EJB is intercepted by a FunctionLoggerInterceptor
  4. That interceptor has a Singleton EJB injectd called DataCacheService
  5. When that Interceptor is called it fails with above exception
  6. When DataCacheService is changed into a Stateless EJB it fails anyway, but the exception is the above refered to Stateless
  7. When we inject a CDI bean instead (which is commented in the code) it works fine
  8. When we comment the EJB injection and uncomment the programmatic lookup to try to find it by JNDI, it works perfectly
@Interceptor
@FunctionLogger
@Priority(Interceptor.Priority.APPLICATION + 5)
public class FunctionLoggerInterceptor {

    private static final Logger LOGGER = Logger.getLogger(FunctionLoggerInterceptor.class.getSimpleName());

    @EJB
    private DataCacheService dataCache;

    //    @Inject
    //    private CDIServiceBean cdiService;

    @AroundInvoke
    public Object logFunctionResult(InvocationContext invocationContext) throws Exception {

        //        Context context = new InitialContext();
        //        DataCacheService ejbService = (DataCacheService) context.lookup("java:global/application/ejbs/DataCacheService");
        //        ejbService.loadCache();

        Long start = System.currentTimeMillis();
        Object result = invocationContext.proceed();
        Long end = System.currentTimeMillis();
        LOGGER.info("Took " + (end - start) + " milliseconds to complete");

        return result;
    }

}

The second project is a WAR packaged application meant to be run in an uber jar in Payara Micro. It can be run by starting it with the payara micro maven plugin after packaging: payara-micro:start

It has a similar structure as the above, except all that code is spread into packages and not modules. With the same endpoint, calling the same beans, it will work just fine. It will find the EJB, inject it and use it.

I did not investigate any possible cause any further because of time constraints, sadly.

Environment

  • Distribution: Server Full - Community Edition version 5.2020.6 to 5.2021.7
  • JDK Version: 1.8.0_282 - AdoptOpenJDK
  • Operating System: Windows 10

CarlosMOGoncalves avatar Aug 27 '21 11:08 CarlosMOGoncalves

Hi @CarlosMOGoncalves,

Would you be able to confirm if you have linked the correct reproducer app as it does not contain the class which you are mentioning? Also, can you provide a simple-to-follow scenario on how to reproduce this on the latest version? A reproducer should ideally follow the SSCCE rules: http://www.sscce.org/. It will greatly help us to find the cause and fix it.

shub8968 avatar Oct 19 '21 11:10 shub8968

Hello @shub8968 ,

Sorry for answering only now. I have checked and can confirm that the reproducer app is indeed correct.

Let me try to be a bit more clear.

This app payara-test-ear has the following module structure:

pom.xml (this is the aggregator module, it is a parent pom) ejbs -> (this module contains some EJBs and CDI beans in pt.test.ejbs.services package and one interceptor in pt.test.ejbs.interceptors) observers ->(this module contains two remote CDI observers that I have used to open another unrelated issue) ear -> (this module is the .ear that creates the archive with the other modules) webservices -> (this module contains a couple of JAX-RS EJB-backed resources in package pt.test.webservices. The other package contains classes that I have used to open another issue) remoteutils ->(this is a .jar but with no uses for this matter

In order to reproduce this issue, one needs to:

  • run mvn clean install on the parent pom.xml
  • deploy it into a Payara Server 5.2020.6+ (the latest I have tried it with was 5.2021.7)
  • invoke the REST endpoint GET http://localhost:8080/webservices/v1/interceptors/intercept which will call the InterceptorResource.java that is under the webservices module, package pt.test.webservices
  • it will throw a javax.ejb.CreateException: Could not create stateless EJB which happens because what I described above.

In short:

  • InterceptorResource has a PeriodicService EJB injected into it which resides in the ejbs module
  • this PeriodicService is intercepted by the FunctionLoggerInterceptor which resides in the same module
  • FunctionLoggerInterceptor has a DataCacheService EJB injected into it which resides in the same module
  • This DataCacheService cannot be found by the injector
  • However, if you uncomment that piece of code which is looking for that bean in the context, via JNDI (and comment the @EJB annotation) it will find the bean
  • If the bean is a CDI bean, like that CDIServiceBean that is commented, it will work perfectly

The other app payara-test-war is simpler:

  • it is a self-contained Payara Micro application
  • it has a similar structure as the one above except there are no modules here, just a plain .war with all its classes in packages instead of spread throughout modules

In order to execute it one needs to:

  • run mvn clean install
  • run mvn payara-micro:start on it
  • invoke the REST endpoint GET http://localhost:20000/payara-test-war/v1/interceptor/intercept
  • this one will run perfectly fine and not throw any exception (you can see it in the log, as it will log a short message), despite the same code structure and logic
  • i.e. it will find the DataCacheService using just the @EJB annotation and not needing to resort to manual JNDI lookup

The behaviour of this Payara Micro .war application is what is expected of the .ear application above.

I have just updated both applications' pom.xml to point to Payara 5.2021.7, but that was just it. It is the same code as before.

Please let me know if you need any further clarification.

And thanks for your time.

CarlosMOGoncalves avatar Oct 25 '21 01:10 CarlosMOGoncalves

Hi @CarlosMOGoncalves,

Thanks for clarifying. The JSR 318 | Interceptors 1.2 ref docs read - "An interceptor instance may be the target of dependency injection. Dependency injection is performed when the interceptor instance is created, using the naming context of the associated target class". However, I have created an internal JIRA with FISH-5888 pertaining to it. Our platform development team will investigate it further. It may take a while to fix the issue. You are free to submit a PR to fix this issue if you feel confident to do so. Thanks.

shub8968 avatar Dec 02 '21 11:12 shub8968

Hello @shub8968

I am sorry if it was a bit confusing. Thanks for proceeding with this. If I am getting it right, it seems the specification does not enforce injection on interceptors, so it becomes a bit of a grey area. I understand. Since it works fine on a a single .war I think it would be really nice if it could also be made to work on .ear based projects.

Thank you for your work, Cheers.

CarlosMOGoncalves avatar Dec 02 '21 11:12 CarlosMOGoncalves