ClassCastException when casting looked-up EJB view

2019-04-22 10:59发布

问题:

I have am deploying 2 EARs onto JBoss AS 7.1.0.Alpha1-SNAPSHOT (post 7.0.1.Final version). Both deploy fine.

I have an EJB Singleton class packaged within a JAR, within one of the EARs:

@Startup
@Singleton
// one of @Local(Store.class), @Remote(Store.class), @LocalBean
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
@Transactional(TransactionPropagation.SUPPORTS)
public class StoreFront implements Store {
...


public interface Store {
...

When it deploys, it says the EJB is bound to:

"java:app/store-core-2011.1.2-SNAPSHOT/StoreFront"
"java:app/store-core-2011.1.2-SNAPSHOT/StoreFront!uk.co.magus.jam.store.core.Store"
"java:module/StoreFront"
"java:module/StoreFront!uk.co.magus.jam.store.core.Store"
"java:global/store-ear-2011.1.2-SNAPSHOT/store-core-2011.1.2-SNAPSHOT/StoreFront!uk.co.magus.jam.store.core.Store"
"java:global/store-ear-2011.1.2-SNAPSHOT/store-core-2011.1.2-SNAPSHOT/StoreFront"

So far, so good. When I try to look it up via JNDI from a non-CDI, non-EJB class within a JAR within the OTHER deployed EAR, it can only be found on the JNDI names under 'global' - again, expected.

However, when I try to cast the resulting object to the actual interface class:

Object lookupObject = new InitialContext().lookup(jndiName);
Store store = (StoreFront)lookupObject;

I get the following exception:

11:17:52,402 ERROR [jam.core.link.LinkListener] (Thread-45) Exception when casting to Store after lookup with [java:global/store-ear-2011.1.2-SNAPSHOT/store-core-2011.1.2-SNAPSHOT/StoreFront]: java.lang.ClassCastException: jam.store.core.Store$$$view1 cannot be cast to jam.store.core.Store
    at jam.core.link.LinkListener.getStore(LinkListener.java:108) [core-jar-2011.1.2-SNAPSHOT.jar:]
    at jam.core.link.LinkListener.postLoad(LinkListener.java:27) [core-jar-2011.1.2-SNAPSHOT.jar:]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [:1.6.0_07]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) [:1.6.0_07]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) [:1.6.0_07]
    at java.lang.reflect.Method.invoke(Method.java:597) [:1.6.0_07]
    at org.hibernate.ejb.event.ListenerCallback.invoke(ListenerCallback.java:48)
    at org.hibernate.ejb.event.EntityCallbackHandler.callback(EntityCallbackHandler.java:96)
    at org.hibernate.ejb.event.EntityCallbackHandler.postLoad(EntityCallbackHandler.java:89)
    at org.hibernate.ejb.event.EJB3PostLoadEventListener.onPostLoad(EJB3PostLoadEventListener.java:49)
    at org.hibernate.engine.internal.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:264)
    at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:1012)
    at org.hibernate.loader.Loader.doQuery(Loader.java:889)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:289)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:259)
    at org.hibernate.loader.Loader.loadEntity(Loader.java:2058)
    at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:81)
    at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:71)
    at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3686)
    at org.hibernate.event.internal.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:446)
    at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:427)
    at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:204)
    at org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:251)
    at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:148)
    at org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:947)
    at org.hibernate.internal.SessionImpl.get(SessionImpl.java:863)
    at org.hibernate.internal.SessionImpl.get(SessionImpl.java:856)
    at org.hibernate.ejb.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:787)
    at org.hibernate.ejb.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:762)
    at org.jboss.as.jpa.container.AbstractEntityManager.find(AbstractEntityManager.java:220) [jboss-as-jpa-7.1.0.Alpha1-SNAPSHOT.jar:7.1.0.Alpha1-SNAPSHOT]
    at jam.core.dao.GenericDAO.findById(GenericDAO.java:87) [core-jar-2011.1.2-SNAPSHOT.jar:]
    at harvest.service.HarvesterDAOUtil.loadLink(HarvesterDAOUtil.java:251) [harvest-sar-2011.1.2-SNAPSHOT.jar:]
    at harvest.service.1779224926$Proxy$_$$_WeldSubclass.loadLink(1779224926$Proxy$_$$_WeldSubclass.java) [harvest-sar-2011.1.2-SNAPSHOT.jar:]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [:1.6.0_07]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) [:1.6.0_07]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) [:1.6.0_07]
    at java.lang.reflect.Method.invoke(Method.java:597) [:1.6.0_07]
    at org.jboss.interceptor.proxy.SimpleInterceptionChain.invokeNextInterceptor(SimpleInterceptionChain.java:112) [jboss-interceptor-core-2.0.0.Alpha3.jar:2.0.0.Alpha3]
    at org.jboss.interceptor.proxy.InterceptorInvocationContext.proceed(InterceptorInvocationContext.java:119) [jboss-interceptor-core-2.0.0.Alpha3.jar:2.0.0.Alpha3]
    at org.jboss.seam.transaction.TransactionInterceptor$1.work(TransactionInterceptor.java:194) [seam-persistence-3.0.0.Final.jar:]
    at org.jboss.seam.transaction.Work.workInTransaction(Work.java:54) [seam-persistence-3.0.0.Final.jar:]
    at org.jboss.seam.transaction.TransactionInterceptor.aroundInvoke(TransactionInterceptor.java:188) [seam-persistence-3.0.0.Final.jar:]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [:1.6.0_07]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) [:1.6.0_07]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) [:1.6.0_07]
    at java.lang.reflect.Method.invoke(Method.java:597) [:1.6.0_07]
    at org.jboss.interceptor.proxy.InterceptorInvocation$InterceptorMethodInvocation.invoke(InterceptorInvocation.java:72) [jboss-interceptor-core-2.0.0.Alpha3.jar:2.0.0.Alpha3]
    at org.jboss.interceptor.proxy.SimpleInterceptionChain.invokeNextInterceptor(SimpleInterceptionChain.java:82) [jboss-interceptor-core-2.0.0.Alpha3.jar:2.0.0.Alpha3]
    at org.jboss.interceptor.proxy.InterceptorMethodHandler.executeInterception(InterceptorMethodHandler.java:133) [jboss-interceptor-core-2.0.0.Alpha3.jar:2.0.0.Alpha3]
    at org.jboss.interceptor.proxy.InterceptorMethodHandler.invoke(InterceptorMethodHandler.java:112) [jboss-interceptor-core-2.0.0.Alpha3.jar:2.0.0.Alpha3]
    at org.jboss.weld.bean.proxy.CombinedInterceptorAndDecoratorStackMethodHandler.invoke(CombinedInterceptorAndDecoratorStackMethodHandler.java:65) [weld-core-1.1.2.Final.jar:2011-07-26 15:02]
    at harvest.service.1779224926$Proxy$_$$_WeldSubclass.loadLink(1779224926$Proxy$_$$_WeldSubclass.java) [harvest-sar-2011.1.2-SNAPSHOT.jar:]
    at harvest.service.CombineHarvester.workOnLinkId(CombineHarvester.java:259) [harvest-sar-2011.1.2-SNAPSHOT.jar:]
    at harvest.service.CombineHarvester.harvestCache(CombineHarvester.java:223) [harvest-sar-2011.1.2-SNAPSHOT.jar:]
    at harvest.service.CombineHarvester.performHarvest(CombineHarvester.java:136) [harvest-sar-2011.1.2-SNAPSHOT.jar:]
    at harvest.service.CombineHarvester.run(CombineHarvester.java:107) [harvest-sar-2011.1.2-SNAPSHOT.jar:]
    at java.lang.Thread.run(Thread.java:619) [:1.6.0_07]

Whether the EJB is annotated with one from any of

@Local(Store.class)
@Remote(Store.class)
@LocalBean 

makes no difference. As I understand it, the fact it's returning a proxy 'view' is normal. However, shouldn't I be able to cast that view to the interface? The combination of which global JNDI name I use and whether I cast to Store or StoreFront also appears to make no difference - unable to cast whatever the combination, even when the exception is like jam.store.core.Store$$$view1 cannot be cast to jam.store.core.Store, with matching (base) class names

Can anyone point out what I'm doing wrong?

回答1:

This is a bug in AS7: https://issues.jboss.org/browse/AS7-1658

One possible workaround is not to cast the returned object, and then use it to fire methods via reflection. Well clunky though.



回答2:

A better approach is to deploy the shared interfaces in a jboss module and include that module in both artifacts' classpath with (when using maven)

                <archive>
                    <manifestEntries>
                        <Dependencies>${jboss.nonjee.modules}</Dependencies>
                    </manifestEntries>
                </archive>


回答3:

Also i get help with this commentary ;):

David Lloyd added a comment - 07/Mar/12 4:02 PM It's because you're using local interfaces. When using local interfaces, you may only have one copy of the interface class (that's what makes it local). Switch to using remote interfaces (or, use Class-Path to get your local interfaces instead of duplicating them) and the problem should disappear.



回答4:

I was getting the same problem; Tried to put the Interface class as a sperate module and include it in one Ear and include it as a provided in the other Ear; In JBOSS the classes in each EAR are loaded by a separate class loader. So if one EAR has Class A , and another has the same Class A , then you get a class cast exception. So in the second EAR give the dependency as provided and in jboss-deployment-structure.xml add the first EAR as a module dependency. Note that the dynamic module dependency might warrant the EAR file name to be constant; You can specify <finalName> under the build tag for the EAR file to fix this

Also changed the lookup from local to remote - see below, and included the maven module containing the interface in both the modules

//jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");

TO
    jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.naming.remote.client.InitialContextFactory");

And you would want to change the lookup from java:app to java:global

final javax.naming.Context context = new InitialContext(jndiProperties);
        AsyncFutureItf  test =(AsyncFutureItf)context.lookup
                //("java:app/Executor/AsyncFutureTest!pacakge.AsyncFutureItf");
                ("java:global/ExecutorEar/Executor/AsyncFutureTest!package.AsyncFutureItf");

And since the Ear is usually registered with the version like this

java:global/ExecutorEar-<version>/Executor/AsyncFutureTest!package.AsyncFutureItf

and you do not want version driven lookup in your code, you need to do two more things. In your application.xml in your EAR builder maven dir (src\main\resources\application.xml) you need to add the "application-name" tag like

    <?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_5.xsd" version="5">
  <description>Task Controller EAR</description>
  <display-name>TaskControllerEAR</display-name>
  <application-name>TaskControllerEAR</application-name>
  <module>
    <ejb>TaskController.jar</ejb>
  </module>
  <library-directory>lib</library-directory>
</application>

and in your pom to generate the pom you need to provide a reference to the application.xml like

<groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-ear-plugin</artifactId>
            <version>2.4.2</version>
            <configuration>
                <version>5</version>
                <defaultLibBundleDir>lib</defaultLibBundleDir>
                <earSourceDirectory>src/main/resources</earSourceDirectory>
                <applicationXml>${project.basedir}/src/main/resources/application.xml</applicationXml>

...

<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.0">
      <ear-subdeployments-isolated>false</ear-subdeployments-isolated>


      <sub-deployment name="MROControllerRest.war">
                <exclusions>
                          <module name="org.apache.commons.logging" />
                          <module name="org.slf4j" />
                          <module name="org.slf4j.ext" />
                          <module name="org.slf4j.jcl-over-slf4j" />
                          <module name="org.slf4j.impl" />
                          <module name="org.apache.log4j" />
                </exclusions>
                <dependencies>
                    <module name="org.slf4j" slot="1.7.5" />
                    <module name="logger" />
                    <module name="deployment.TaskControllerEAR.ear.TaskController.jar" export="TRUE"/>

                </dependencies>

Here is the stack trace for reference

java.lang.ClassCastException: com.package.TaskSplitterItf$$$view210 cannot be cast to com.package.TaskSplitterItf


回答5:

Same issue too if your EJB-client is in an EAR and the EJB implementation in another "service"-EAR, and you invoke a @Remote Interface method on the EJB returning a complex Object instead of a primitive type. The complex Object being returned is also declared as an Interface, properly declared, known, and available to the EJB-client. The returned Object implementation is, it, contained in the "service"-EAR along with the target EJB implementation too. Despite such conformant code, JBoss fails on Class cast exception.

I noted that you can indeed keep a @Remote Interface for declaring all target EJB methods, but can only use a plain class declaration for all the Objects returned by those methods. If you declare the returned Objects as Interfaces, Class cast exceptions ensue in JBoss.

This is a limitation (or bug) in JBoss; it does work in Glassfish and WebLogic where we had the same code once running.



回答6:

Same Problem occurred if you have the jar file with your EJBs more as one time in your EAR.

Sample:

You have

  • a myEJB.jar file which contains all of your EJB
  • a myWebApp.war which contains your web classes / resources
  • a myEnterprise.ear which contains myEJB.jar and myWebApp.jar

If your myWebApp.war also contains the myEJB.jar by self then you will have this error (ClassCastExc). Seems that the EJB object is created inside the myEJB.jar of the myEnterprise.ear. And if you then will be cast this object into the same class of the myEJB.jar inside of the myWebApp.war this will not worked because this is class is defined in another jar.

If you have this error you must remove the myEJB.jar from your war file and the ClassCastExc is gone ...