Java EE: Proxy cannot be cast to Local Interface,

2020-04-12 07:24发布

I'm currently "rearranging" my Java EE application, which consists of three components:

  • MyAppInterface: mostly JPA- and JAXB-annotated POJOs, also some EJB Local Interfaces
  • MyAppServer: JPA Facades, EJBs, Jersey resources
  • MyAppWeb: GWT frontend, communicates with MyAppServer via HTTP/REST via loadbalancer

Both MyAppServer and MyAppWeb use the classes defined in MyAppInterface; MyAppServer "exports" some of its EJBs via local interfaces in MyAppInterface. MyAppInterface is kind of the API, it's what you need to work with MyAppServer.

In Maven I am packaging MyAppInterface as jar, both MyAppServer and MyAppWeb are packaged as war with a dependency on MyAppInterface in a compile scope. So MyAppInterface.jar ends up in both war-files.

When I deploy both war-files as separate applications on the same Glassfish, deployment is successful. The JAXB-Jersey-powered communication between them works, so I have to assume that both applications can classload MyAppInterface.

But in one case, I'd like to access a MyAppServer-EJB from MyAppWeb. The JNDI-lookup via InitialContext works, but when I try to cast the obtained proxy to the local interface, I get a ClassCast exception:

com.sun.enterprise.container.common.spi.util.InjectionException: Error creating managed object for class: class com.skalio.bonusapp.web.server.StoreServiceImpl
    at com.sun.enterprise.container.common.impl.util.InjectionManagerImpl.createManagedObject(InjectionManagerImpl.java:315)
    at com.sun.enterprise.web.WebContainer.createServletInstance(WebContainer.java:717)
    at com.sun.enterprise.web.WebModule.createServletInstance(WebModule.java:1959)
...
Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
    at com.sun.enterprise.container.common.impl.util.InjectionManagerImpl.createManagedObject(InjectionManagerImpl.java:307)
    ... 28 more
Caused by: java.lang.ClassCastException: $Proxy365 cannot be cast to com.skalio.bonusapp.beans.SettingsBeanLocal
    at com.skalio.bonusapp.web.server.StoreServiceImpl.getServerSettings(StoreServiceImpl.java:85)
    at com.skalio.bonusapp.web.server.StoreServiceImpl.(StoreServiceImpl.java:47)
    ... 33 more

Google makes me believe that this could be a classloading issue, possibly related to the the duplicate inclusion of MyAppInterface.jar. Is that correct? What do you suggest to do?

How would you distribute and package the components?

Note: At the moment I'd like to avoid creating an EAR and rather keep flexibility of choosing what components to deploy where...

1条回答
我命由我不由天
2楼-- · 2020-04-12 08:00

Yes, for local EJB, the interface JAR needs to be in a shared location for class loading. The EJB spec actually doesn't require local EJBs to be accessible across applications (or standalone modules), but most application servers implement local EJB with simple proxies.

You either need to package your modules in an EAR with the JAR in the lib directory, or you need to arrange for the JAR to be loaded by a product-wide class loader that is visible to both applications.

查看更多
登录 后发表回答