Avatar billede fredand Forsker
02. januar 2023 - 19:38 Der er 8 kommentarer

Problems when I use instanceof together with @EJB, why is that?

I got two interface-classes (actually three) in a maven project called "interface":

    @Local
    public interface CrudSessionLocal extends CRUDS {
       
    }

    public interface ExceptionThrower {
       
        public boolean isThrowException();

        public void setThrowException(boolean throwException);

    }

I got a bean in a maven project called "server" (that got dependency to "interface"-project) that implements these interfaces:

    @Stateless
    @TransactionManagement(TransactionManagementType.CONTAINER)
    public class CrudsTransactionSessionBean implements CrudSessionLocal, ExceptionThrower {

I got a servlet in a maven project called "web-client" (that got dependency to "interface"-project, but not to "server"-project). The servlet uses the bean like this code below, remark that the declaration uses the interface.

    @EJB(beanName = "CrudsTransactionSessionBean")
    private CrudSessionLocal crudSessionLocal;

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        if( crudSessionLocal instanceof ExceptionThrower ) {
            ExceptionThrower exceptionThrower = (ExceptionThrower)crudSessionLocal;
            exceptionThrower.setThrowException(throwException);
        }


I thought that use of the interface in the declartion of the bean-member in the servlet is a good practice. For eg I thought that I can reuse the interface.jar and web-client.jar in an other ear but with an other serverlayer like other_server.jar that holds an other bean that does something else as long as the bean is called "CrudsTransactionSessionBean" and implements the interface.

In this case the bean also implements the interface ExceptionThrower. The idea is to check in the servlet if the bean is an instanceof ExceptionThrower, then an exception shall be thrown.

But to my surprise, at runtime, the member-bean is NOT an instance of ExceptionThrower. It is:
"com.myapp.cruds.server.CrudsTransactionSessionBean_d7q6e8_CrudSessionLocalImpl@617fe9e5"
(That sounds like a CrudsTransactionSessionBean to me and that implements ExceptionThrower)


I think I can do it like this, but then I will not acomplish the "loose coupling" between the jar-dependecies.

    @EJB(beanName = "CrudsTransactionSessionBean")
    private CrudsTransactionSessionBean crudSessionLocal;

Do you guys see why this is happening?
And to accomplish the "loose coupling" how should I solve it?

Btw I use Java 8 and Weblogic 14

Best regards
Fredrik
Avatar billede arne_v Ekspert
02. januar 2023 - 20:19 #1
I think there are two potential causes for the problem:

1) "classic" problem that class X loaded by classloader A is not the same class as class X loaded by classloader B

2) you do not get an instance of CrudsTransactionSessionBean  but an instance of a generated class that implements CrudSessionLocal and delegates to an instance of CrudsTransactionSessionBean, but does not implement  ExceptionThrower.

Based on the error message then the second sounds most likely.

To verify try dumping the type and all super types of the crudSessionLocal variable.
Avatar billede arne_v Ekspert
02. januar 2023 - 20:20 #2
As a quick workaround: could CRUDS extend ExceptionThrower?
Avatar billede fredand Forsker
02. januar 2023 - 20:44 #3
I will try to dump out all info, but you might be correct that I get a "delegator" back. I thought of the CRUDS extend ExceptionThrower as well, and I think you are right. Right now that might be the only solution. At least the quickest!
Avatar billede fredand Forsker
02. januar 2023 - 20:57 #4
The following code puts out this.

        String name = crudSessionLocal.getClass().getName();
        Class<?>[] interfaces = crudSessionLocal.getClass().getInterfaces();
        System.out.println();
        System.out.println("Class: " + name);
        for (int i = 0; i < interfaces.length; i++) {
            System.out.println("-interface: " + interfaces[i].getName());
        }
       
        String name2 = crudSessionLocal.getClass().getSuperclass().getName();
        Class<?>[] interfaces2 = crudSessionLocal.getClass().getSuperclass().getInterfaces();
        System.out.println();
        System.out.println("Class: " + name2);
        for (int i = 0; i < interfaces2.length; i++) {
            System.out.println("-interface: " + interfaces2[i].getName());
        }
       

Class: com.myapp.cruds.server.CrudsTransactionSessionBean_d7q6e8_CrudSessionLocalImpl
-interface: com.myapp.cruds.interfaces.CrudSessionLocal
-interface: weblogic.ejb.container.interfaces.Invokable
-interface: weblogic.ejb.container.interfaces.WLLocalEJBObject

Class: java.lang.Object

So it looks like the only solution, as you said, is to let CRUDS extend ExceptionThrower.
Avatar billede fredand Forsker
06. januar 2023 - 22:34 #5
Hello,

It was a really surprise to me that the app-server did not implement all of the interfaces specified in the session bean in the proxy-implementation. (It was actually a surprise that it was a proxy and not the bean that was injected, and I have bean in the biz for over 20 years. Wow, new lesson each day)

I think I solved it pretty good by using Qualifier. I found this great tutorial: http://buraktas.com/create-qualifiers-cdi-beans/

Using that example I put a Qualifier-annotation class in my interface-project

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
public @interface ExceptionThrowerQualifier {
}
I marked a bean with the qualifier in the server-project:

@ExceptionThrowerQualifier
@Stateless
@TransactionManagement(TransactionManagementType.CONTAINER)
public class CrudsTransactionSessionBean implements CrudSessionLocal, ExceptionThrower {
In my servlet in the web-client-project I Inject it like this:

@Inject
@ExceptionThrowerQualifier
private CrudSessionLocal crudSessionLocal;

protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {

    if( crudSessionLocal instanceof ExceptionThrower ) {
        ExceptionThrower exceptionThrower = (ExceptionThrower)crudSessionLocal;
        exceptionThrower.setThrowException(throwException);
    }

The benifit is that the web-client do not need to be aware of the implementation ("loose coupling" between server and web-client, right?). I could just use the interface classes from the web-client I do not need to implement the ExceptionThrower in the server. This is nice so I could use "instanceof" if I want to.

But a big drawback is that at least one class need to be annotated with @ExceptionThrowerQualifier. Else it seems like the app server do not know which class to use for injection. (Perhaps this is only true if there are more then one class that implements the interface CrudSessionLocal.)

Best regards Fredrik
Avatar billede fredand Forsker
06. januar 2023 - 22:53 #6
Looks like I forgot to mention one thing:

@Local
public interface ExceptionThrower {

To make it work this seems to be needed.
Avatar billede arne_v Ekspert
07. januar 2023 - 01:02 #7
Nice - I had not ever heard about those qualifiers.
Avatar billede arne_v Ekspert
07. januar 2023 - 01:07 #8
I don't think the whole interface vs delegate class vs bean class should be so surprising.

Remember *bad* old EJB 1.x and 2.x?

When you did a lookup of your interface then you did not get am instance of the actual bean class but an instance of a delegating class.

That silly spec did not even allow the bean class to implement the interface.
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester