Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remote CDI events throw Exception instead of triggering the observers #1506

Closed
Erates opened this issue Apr 3, 2017 · 17 comments
Closed

Remote CDI events throw Exception instead of triggering the observers #1506

Erates opened this issue Apr 3, 2017 · 17 comments
Assignees
Labels
Type: Bug Label issue as a bug defect
Milestone

Comments

@Erates
Copy link

Erates commented Apr 3, 2017

Description


Remote CDI Events don't work (or I'm not implementing it as I should).

Expected Outcome

I expect that when I fire a remote CDI event, all Observers get notified and their method get triggered.

Current Outcome

The following Exception is thrown when Payara tries to trigger the remote Observers. The exception is thrown for every observable.

[2017-04-03T07:17:48.210+0200] [Payara 4.1] [SEVERE] [] [javax.enterprise.system.container.ejb.org.glassfish.ejb.security.application] [tid: _ThreadID=75 _ThreadName=hz._hzInstance_1_development.event-5] [timeMillis: 1491196668210] [levelValue: 1000] [[
  SECEJB9000: Exception while running pre-invoke
java.lang.ClassCastException: org.glassfish.api.invocation.ComponentInvocation cannot be cast to com.sun.ejb.EjbInvocation
	at org.glassfish.ejb.security.application.EjbSecurityComponentInvocationHandler$1.beforePreInvoke(EjbSecurityComponentInvocationHandler.java:76)
	at org.glassfish.api.invocation.InvocationManagerImpl.preInvoke(InvocationManagerImpl.java:181)
	at fish.payara.micro.cdi.extension.ClusteredCDIEventBusImpl.eventReceived(ClusteredCDIEventBusImpl.java:181)
	at fish.payara.appserver.micro.services.PayaraInstance.receiveMessage(PayaraInstance.java:167)
	at fish.payara.nucleus.eventbus.TopicListener.onMessage(TopicListener.java:56)
	at com.hazelcast.topic.impl.TopicService.dispatchEvent(TopicService.java:134)
	at com.hazelcast.spi.impl.eventservice.impl.LocalEventDispatcher.run(LocalEventDispatcher.java:56)
	at com.hazelcast.util.executor.StripedExecutor$Worker.process(StripedExecutor.java:187)
	at com.hazelcast.util.executor.StripedExecutor$Worker.run(StripedExecutor.java:171)
]]

[2017-04-03T07:17:48.211+0200] [Payara 4.1] [SEVERE] [] [com.hazelcast.spi.impl.eventservice.impl.EventServiceImpl] [tid: _ThreadID=75 _ThreadName=hz._hzInstance_1_development.event-5] [timeMillis: 1491196668211] [levelValue: 1000] [[
  [172.20.0.56]:5900 [development] [3.7.4] hz._hzInstance_1_development.event-5 caught an exception while processing task:com.hazelcast.spi.impl.eventservice.impl.LocalEventDispatcher@7b73507e
org.glassfish.api.invocation.InvocationException
	at org.glassfish.api.invocation.InvocationManagerImpl.postInvoke(InvocationManagerImpl.java:215)
	at fish.payara.micro.cdi.extension.ClusteredCDIEventBusImpl.eventReceived(ClusteredCDIEventBusImpl.java:207)
	at fish.payara.appserver.micro.services.PayaraInstance.receiveMessage(PayaraInstance.java:167)
	at fish.payara.nucleus.eventbus.TopicListener.onMessage(TopicListener.java:56)
	at com.hazelcast.topic.impl.TopicService.dispatchEvent(TopicService.java:134)
	at com.hazelcast.spi.impl.eventservice.impl.LocalEventDispatcher.run(LocalEventDispatcher.java:56)
	at com.hazelcast.util.executor.StripedExecutor$Worker.process(StripedExecutor.java:187)
	at com.hazelcast.util.executor.StripedExecutor$Worker.run(StripedExecutor.java:171)
]]

Steps to reproduce (Only for bug reports)

1 -** Enable Hazelcast**
default-config -> hazelcast -> check the "enabled" box
2 -** Set Hazelcast as the availability service**
default-config -> availability service -> web- and ejb- container -> Persistence type: hazelcast
3 -** Deploy the Remote EJB application**
4 -** Deploy the other application(s)**
5 -** Trigger the Remote CDI event**

Samples

All applications (The remote application and the 'normal' applications) have the following maven dependency:

<dependency>
	<groupId>fish.payara.api</groupId>
	<artifactId>payara-api</artifactId>
	<version>4.1.1.171.1</version>
	<scope>provided</scope>
</dependency>

Remote EJB application:

@Singleton
@LocalBean
public class MyEventTriggerBean {

    @Inject
    @Outbound(loopBack = true) // loopBack true is required if the observing application is deployed on the same Payara Server instance
    Event<MyPayloadPOJO> event;

    private Map<String, CsnDto> map;

    public MyEventTriggerBean() {
    }

    public void doTheTrigger(String myContent) {
        event.fire(new MyPayloadPOJO(myContent));
    }
}

My listeners are coded like this:

public void handleEvent(@Observes @Inbound MyPayloadPOJO event) {
    logger.info("Received MyPayloadPOJO: " + event.getPayload());
}

The applications look like this:

  • Remote EJB (The observable)
  • EAR 1
    • Common EJB
      • Observer 1 (with the listener code)
    • App 1 EJB
      • Observer 2 (with the listener code)
    • App 1 WEB
      • Observer 3 (with the listener code)
      • WEB-INF/lib/ Common WEB jar
        • Observer 4 (with the listener code)
  • EAR 2
    • Common EJB
      • Observer 5 (with the listener code)
    • App 2 EJB
      • Observer 6 (with the listener code)
    • App 2 WEB
      • Observer 7 (with the listener code)
      • WEB-INF/lib/ Common WEB jar
        • Observer 8 (with the listener code)

Environment

@smillidge
Copy link
Contributor

Can you try on an ApplicationScoped CDI bean rather than an EJB?

@lprimak
Copy link
Contributor

lprimak commented Apr 3, 2017

I wouldn't think CDI event bus would work across remote EJBs, as it's inherently local. You should use JMS for tasks like this, which is what it's meant to do

@smillidge
Copy link
Contributor

this is the clustered cdi event bus which works across Hazelcast.

@lprimak
Copy link
Contributor

lprimak commented Apr 3, 2017

Right, @smillidge but I still woudn't think it would work with remote EJBs

@smillidge
Copy link
Contributor

It's not a remote EJB just a remote EJB application. However I think it needs to be an ApplicationScoped CDI bean for it to work.

@Erates
Copy link
Author

Erates commented Apr 4, 2017

With an ApplicationScoped bean, I still get the exception:

[2017-04-04T07:35:39.090+0200] [Payara 4.1] [INFO] [] [] [tid: _ThreadID=51 _ThreadName=iiop-service-kernel(1) SelectorRunner] [timeMillis: 1491284139090] [levelValue: 800] [[
  Cannot find the resource bundle for the name com.sun.logging.enterprise.system for class org.glassfish.enterprise.iiop.api.ORBLazyServiceInitializer using org.glassfish.main.orb.connector [239]]]

[2017-04-04T07:35:39.897+0200] [Payara 4.1] [INFO] [] [] [tid: _ThreadID=194 _ThreadName=orb-thread-pool-1 (pool #1): worker-16] [timeMillis: 1491284139897] [levelValue: 800] [[
  Cannot find the resource bundle for the name com.sun.logging.enterprise.system.core.naming for class org.glassfish.concurrent.runtime.deployer.ConcurrentObjectFactory using org.glassfish.main.concurrent.impl [41]]]

[2017-04-04T07:35:39.925+0200] [Payara 4.1] [SEVERE] [] [javax.enterprise.system.container.ejb.org.glassfish.ejb.security.application] [tid: _ThreadID=73 _ThreadName=hz._hzInstance_1_development.event-5] [timeMillis: 1491284139925] [levelValue: 1000] [[
  SECEJB9000: Exception while running pre-invoke
java.lang.ClassCastException: org.glassfish.api.invocation.ComponentInvocation cannot be cast to com.sun.ejb.EjbInvocation
	at org.glassfish.ejb.security.application.EjbSecurityComponentInvocationHandler$1.beforePreInvoke(EjbSecurityComponentInvocationHandler.java:76)
	at org.glassfish.api.invocation.InvocationManagerImpl.preInvoke(InvocationManagerImpl.java:181)
	at fish.payara.micro.cdi.extension.ClusteredCDIEventBusImpl.eventReceived(ClusteredCDIEventBusImpl.java:181)
	at fish.payara.appserver.micro.services.PayaraInstance.receiveMessage(PayaraInstance.java:167)
	at fish.payara.nucleus.eventbus.TopicListener.onMessage(TopicListener.java:56)
	at com.hazelcast.topic.impl.TopicService.dispatchEvent(TopicService.java:134)
	at com.hazelcast.spi.impl.eventservice.impl.LocalEventDispatcher.run(LocalEventDispatcher.java:56)
	at com.hazelcast.util.executor.StripedExecutor$Worker.process(StripedExecutor.java:187)
	at com.hazelcast.util.executor.StripedExecutor$Worker.run(StripedExecutor.java:171)
]]

[2017-04-04T07:35:39.926+0200] [Payara 4.1] [SEVERE] [] [com.hazelcast.spi.impl.eventservice.impl.EventServiceImpl] [tid: _ThreadID=73 _ThreadName=hz._hzInstance_1_development.event-5] [timeMillis: 1491284139926] [levelValue: 1000] [[
  [172.20.0.56]:5900 [development] [3.7.4] hz._hzInstance_1_development.event-5 caught an exception while processing task:com.hazelcast.spi.impl.eventservice.impl.LocalEventDispatcher@4a714c9e
org.glassfish.api.invocation.InvocationException
	at org.glassfish.api.invocation.InvocationManagerImpl.postInvoke(InvocationManagerImpl.java:215)
	at fish.payara.micro.cdi.extension.ClusteredCDIEventBusImpl.eventReceived(ClusteredCDIEventBusImpl.java:207)
	at fish.payara.appserver.micro.services.PayaraInstance.receiveMessage(PayaraInstance.java:167)
	at fish.payara.nucleus.eventbus.TopicListener.onMessage(TopicListener.java:56)
	at com.hazelcast.topic.impl.TopicService.dispatchEvent(TopicService.java:134)
	at com.hazelcast.spi.impl.eventservice.impl.LocalEventDispatcher.run(LocalEventDispatcher.java:56)
	at com.hazelcast.util.executor.StripedExecutor$Worker.process(StripedExecutor.java:187)
	at com.hazelcast.util.executor.StripedExecutor$Worker.run(StripedExecutor.java:171)
]]

The one firing the CDI event is no remote EJB, but I do talk to the application using a remote EJB. So remote EJB -> ApplicationScoped -> fires CDI event -> throws exception.

To test this I got a local client application that searches for the remote EJB, and calls the method that calls the ApplicationScoped bean.

It seems like it cannot talk to Hazelcast.

Maybe my configuration is faulty?

@lprimak
Copy link
Contributor

lprimak commented Apr 4, 2017

There is a limitation in the remote clustering functionality that it can only be invoked from a CDI bean, not EJB. Since the "invocation context" that you have EJB, you are hitting that limitation.
This most likely needs to be fixed, and there is most likely a workaround, but I don't know one off-hand right now

@lprimak
Copy link
Contributor

lprimak commented Apr 5, 2017

@smillidge I have thought about this a bit and this would also be an issue within Hz serializer code.
I will create an internal issue for this and fix it, along with some refactoring so CDI event code uses InvocationContextUtil code that handles both Servlet and EJB invocations

@lprimak lprimak self-assigned this Apr 5, 2017
@lprimak lprimak added 1:Investigating Status: Accepted Confirmed defect or accepted improvement to implement, issue has been escalated to Platform Dev Type: Enhancement Label issue as an enhancement request labels Apr 5, 2017
@lprimak
Copy link
Contributor

lprimak commented Apr 5, 2017

Assigned internal issue PAYARA-1566

@Erates
Copy link
Author

Erates commented Apr 5, 2017

Will this fix include the fact that the observable and the observers are in separate jar(war) files on different levels in the classloader?

@lprimak
Copy link
Contributor

lprimak commented Apr 5, 2017

Yes, it should

@smillidge
Copy link
Contributor

smillidge commented Apr 5, 2017

Can you post a reproducible test case showing how you are initializing the CDI event bus in each application deployment?

@Erates
Copy link
Author

Erates commented Apr 5, 2017

Download the zip file that contains all the ear files and maven projects
CDI_Error.zip

I have only included 1 local application and 1 local observer, but for every observer inside every application, the stacktrace get thrown.

  1. Install a new Payara 171.1
  2. Start the payara instance, and enable Hazelcast (and change the web and ejb layer availability service to Hazelcast)
  3. Restart the payara instance
  4. Deploy the CDI_Error_Remote-ear file
  5. Deploy the LocalApplication-ear file
  6. Browse to http://localhost:8080/LocalApplication-web/
  7. Enter something in the inputfield and press "Trigger event"
  8. Stack trace appears in server.log:
[2017-04-05T12:31:13.925+0200] [Payara 4.1] [SEVERE] [] [javax.enterprise.system.container.ejb.org.glassfish.ejb.security.application] [tid: _ThreadID=68 _ThreadName=hz._hzInstance_1_development.event-5] [timeMillis: 1491388273925] [levelValue: 1000] [[
  SECEJB9000: Exception while running pre-invoke
java.lang.ClassCastException: org.glassfish.api.invocation.ComponentInvocation cannot be cast to com.sun.ejb.EjbInvocation
	at org.glassfish.ejb.security.application.EjbSecurityComponentInvocationHandler$1.beforePreInvoke(EjbSecurityComponentInvocationHandler.java:76)
	at org.glassfish.api.invocation.InvocationManagerImpl.preInvoke(InvocationManagerImpl.java:181)
	at fish.payara.micro.cdi.extension.ClusteredCDIEventBusImpl.eventReceived(ClusteredCDIEventBusImpl.java:181)
	at fish.payara.appserver.micro.services.PayaraInstance.receiveMessage(PayaraInstance.java:167)
	at fish.payara.nucleus.eventbus.TopicListener.onMessage(TopicListener.java:56)
	at com.hazelcast.topic.impl.TopicService.dispatchEvent(TopicService.java:134)
	at com.hazelcast.spi.impl.eventservice.impl.LocalEventDispatcher.run(LocalEventDispatcher.java:56)
	at com.hazelcast.util.executor.StripedExecutor$Worker.process(StripedExecutor.java:187)
	at com.hazelcast.util.executor.StripedExecutor$Worker.run(StripedExecutor.java:171)
]]

[2017-04-05T12:31:13.926+0200] [Payara 4.1] [SEVERE] [] [com.hazelcast.spi.impl.eventservice.impl.EventServiceImpl] [tid: _ThreadID=68 _ThreadName=hz._hzInstance_1_development.event-5] [timeMillis: 1491388273926] [levelValue: 1000] [[
  [172.20.5.130]:5900 [development] [3.7.4] hz._hzInstance_1_development.event-5 caught an exception while processing task:com.hazelcast.spi.impl.eventservice.impl.LocalEventDispatcher@3bb347e
org.glassfish.api.invocation.InvocationException
	at org.glassfish.api.invocation.InvocationManagerImpl.postInvoke(InvocationManagerImpl.java:215)
	at fish.payara.micro.cdi.extension.ClusteredCDIEventBusImpl.eventReceived(ClusteredCDIEventBusImpl.java:207)
	at fish.payara.appserver.micro.services.PayaraInstance.receiveMessage(PayaraInstance.java:167)
	at fish.payara.nucleus.eventbus.TopicListener.onMessage(TopicListener.java:56)
	at com.hazelcast.topic.impl.TopicService.dispatchEvent(TopicService.java:134)
	at com.hazelcast.spi.impl.eventservice.impl.LocalEventDispatcher.run(LocalEventDispatcher.java:56)
	at com.hazelcast.util.executor.StripedExecutor$Worker.process(StripedExecutor.java:187)
	at com.hazelcast.util.executor.StripedExecutor$Worker.run(StripedExecutor.java:171)
]]

@smillidge smillidge added Type: Bug Label issue as a bug defect and removed Type: Enhancement Label issue as an enhancement request labels Apr 17, 2017
@smillidge
Copy link
Contributor

This is reproducible.

Internal issue number is PAYARA-1583. The problem arises in the Remote EJB application as the event is looped back to this application and the ClusteredCDIEventBus in that application. There is a bug whereby if the Clustered CDI Event Bus is initialised in an ejb jar the error occurs.

@fturizo fturizo added 3:DevInProgress and removed Status: Accepted Confirmed defect or accepted improvement to implement, issue has been escalated to Platform Dev labels Apr 17, 2017
@smillidge
Copy link
Contributor

smillidge commented Apr 17, 2017

Your example application will still have problems with the fix. In particular because the receiver bean is Session Scoped. As the messages are fired remotely there is no concept of "active session" when a remote event is received in the CDI Event Bus. So even with the fix your bean will not receive the event as it is not in the active CDI scope. An Application Scoped Bean will receive the event.

@Erates
Copy link
Author

Erates commented Apr 18, 2017

I think it's normal that an event will not trigger inactive beans. But when I have active Session Scoped beans, (because a clicked on something or opened a page) shouldn't those beans be triggered? At that point they are in an active scope. Or am I wrong?

If this is not solvable, how would you make it that Session (or View) Scoped beans can be updated from external events?

@smillidge
Copy link
Contributor

smillidge commented Apr 18, 2017

You may potentially have a lot of Session Scoped beans as they are tied to a user's session. Therefore there is no easy way to map the event back to a specific user's session when receiving an event from the event bus. It may be possible to activate the correct session scope by passing some application specific payload into the event and the ApplicationScoped bean. Alternatively you could keep some mapping between SessionID and the data in an Application Scoped Bean and pass the session ID over the event.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Bug Label issue as a bug defect
Projects
None yet
Development

No branches or pull requests

5 participants