Generics Support in WebLogic 10.3.2 and Fast-Swap

Generics are officially supported in EJB3 by Oracle WebLogic 10.3.2 (http://download.oracle.com/docs/cd/E11035_01/wls100/ejb30/implementing.html), but this support come with many bugs that can make you spend a lot of time with strange errors like this:

java.io.IOException: JDT compilation error!

    at weblogic.ejb.container.ejbc.CompilerForJDT.compile(CompilerForJDT.java:66)

    at weblogic.ejb.container.ejbc.EJBCompiler.doCompile(EJBCompiler.java:358)

    at weblogic.ejb.container.ejbc.EJBCompiler.compileEJB(EJBCompiler.java:556)

    at weblogic.ejb.container.ejbc.EJBCompiler.compileEJB(EJBCompiler.java:523)

    at weblogic.ejb.container.deployer.EJBDeployer.runEJBC(EJBDeployer.java:441)

To better understand the issue now let's look at a very short example taken from a real production case. In this example we have a normal class hierarchy that implements the Generic Repository Pattern with EJB3. Many details are omitted here, including the usual JPA life cycle methods that are part of the Repository, and the injection of 'EntityManager' in JpaRepository. 

In any case these classes should be deployable as an EJB3 Module (you can download the complete working Maven project here): 

public class ApplicationContext {

}

@Stateless

public class ApplicationContextJpaRepository extends MusicStoreJpaRepository implements ApplicationContextRepositoryLocal {

}

public interface ApplicationContextRepositoryLocal extends Repository<ApplicationContext> {

}

public interface Repository<ENTITY> {

    ENTITY findById(Object id);

}

public abstract class JpaRepository<ENTITY>  implements Repository<ENTITY> {

    EntityManager entityManager;

    public ENTITY findById(Object id) {

        return getEntityManager().find(getEntityClass(), id);

    }

    protected abstract Class<ENTITY> getEntityClass();

    protected EntityManager getEntityManager() {

        return entityManager;

    }

    protected Query createNamedQuery(String jpql) {

        return getEntityManager().createNamedQuery(jpql);

    }

    protected Query createQuery(String jpql) {

        return getEntityManager().createQuery(jpql);

    }

}

public class MusicStoreJpaRepository extends JpaRepository<ApplicationContext>{

    @Override

    protected Class<ApplicationContext> getEntityClass() {

        return ApplicationContext.class;

    }

}

Among others here we have used Oracle's suggestions (http://download.oracle.com/docs/cd/E11035_01/wls100/ejb30/implementing.html # wp1129878) regarding the use of generics on EJB3, creating a "useless" interface "ApplicationContextRepositoryLocal" that is implement by the Bean class instead of directly implementing the generic interface. 

Enabling fast-swap for fast development we have three types of problems.

First problem: wrong business methods as EJB methods 

WebLogic APPC miscalculates reading the business methods and instead of taking them from the business interface (as written in EJB 3.0-final spec, paragraph 4.6.6) infer them from the implementation (this even without fast-swap !!!!). 

Second problem: protected Methods made public

WebLogic APPC is still wrong, trying to infer business method from our protected implementation method. Infact going to see the classes generated by APPC in your "${domain.name}\servers\${server.name}\cache\EJBCompilerCache\${ejb.module.hash}", we have an internal parallel hierarchy with an interface: 

public interface ApplicationContextJpaRepository_hskm2o_Intf

  extends WLEnterpriseBean

{

    ...

    public javax.persistence.EntityManager getEntityManager();

    ...

 

which is implemented by: 

public final class ApplicationContextJpaRepository_hskm2o_Impl

    extends ApplicationContextJpaRepository

  implements weblogic.ejb.container.interfaces.WLEnterpriseBean,

         ApplicationContextJpaRepository_hskm2o_Intf

{

    ...

 

But in the example code: 

a) The method getEntityManager is not part of the business interface, but only of the internal implementation of the Bean 

b) It is also declared "protected abstract" in the superclass class Bean!! 

Obviously these generated classes cannot be compiled (you can't decrease the visibility of a method):

<Compilation Error> ApplicationContextJpaRepository_hskm2o_Impl.java: The inherited method JpaRepository<ApplicationContext>.getEntityManager() cannot hide the public abstract method in ApplicationContextJpaRepository_hskm2o_Intf

<Compilation Error> ApplicationContextJpaRepository_hskm2o_Impl.java: The inherited method ApplicationContextJpaRepository.faiQualcosa() cannot hide the public abstract method in ApplicationContextJpaRepository_hskm2o_Intf

<Compilation Error> ApplicationContextJpaRepository_hskm2o_Impl.java: The inherited method MusicStoreJpaRepository.getEntityClass() cannot hide the public abstract method in ApplicationContextJpaRepository_hskm2o_Intf

<Compilation Error> ApplicationContextJpaRepository_hskm2o_Impl.java: The inherited method JpaRepository<ApplicationContext>.createQuery(String) cannot hide the public abstract method in ApplicationContextJpaRepository_hskm2o_Intf

<Compilation Error> ApplicationContextJpaRepository_hskm2o_Impl.java: The inherited method JpaRepository<ApplicationContext>.createNamedQuery(String) cannot hide the public abstract method in ApplicationContextJpaRepository_hskm2o_Intf> 

The solution to this problem is:

"Never declare protected methods in superclasses of the bean implementation class"

Third problem: generics interfaces with lost type parameter

APPC compiler is broken also when it has to declare methods with generic parameters or return value. In the same interface we can read: 

public interface ApplicationContextJpaRepository_hskm2o_Intf

  extends WLEnterpriseBean

{

    ...

    public java.lang.Object findById(java.lang.Object arg0);

    ...

the generic return value is lost and the previous generated method is unable to compile the EJB, because in the skeleton code we have: 

testcaseoracle.repository.ApplicationContext result = null;

...

...

...

result =  __bean.findById(arg0);

but because "__bean" is declared as a "ApplicationContextJpaRepository_hskm2o_Intf" we get a compile-time error (we can not assign an Object to an ApplicationContext without casting) at deploy time:

<Compilation Error> ApplicationContextJpaRepository_hskm2o_ApplicationContextRepositoryLocalImpl.java: Type mismatch: cannot convert from Object to ApplicationContext

The solution to this problem is something that go against what we read on Oracle's documentation

"Never have a hierarchy deeper then 3, included Object, in your bean implementation class"

In this example is enough to eliminate the MusicStoreJpaRepository and let the bean to directly implement the generic JpaRepository and the interface is generated correctly. 

Fast Swap is the problem, also on Web Modules

Fast-Swap is a great enhancement for enterprise developer but it's still buggy. I think that the problem rely inside the fact that an application using it has a different classloader, the "com.bea.wls.redef.RedefiningClassLoader", which is buggy also in Web components. Here is unable to read debug symbols from the .class files and when you enter in a debug session you are unable to declare breakpoint on arbitrary lines, only methods (which are a part of the class which is never lost, of course !!).

To avoid this annoying bug you can enable fast-swap in the ear and disable it on web-modules

I don't know why, but this way the classloader use is the good old ChangeAwareClassLoader but the fast-swap still works.

Conclusions

All of these problems have a single, simple conclusion that goes against one of the most interesting new feature of WebLogic 10.3:

"Fast Swap is buggy !!!! Long life Fast-Swap"

With these simple advise you can work great with it, and be really more productive in your development life-cycle.

Appendix A: enable human readable error messages

If you want to see what is really going wrong in your EJB instead of useless IOException, then raise your log level to DEBUG and then enable these two debug flags DebugEjbCompilation and DebugEjbDeployment, both of them from the Admin Console.

Appendix B: Other references to similar problems:

From Oracle's "resolved issues" http://download.oracle.com/docs/cd/E12840_01/wls/docs103/issues/known_resolved.html # wp1174792 (search CR366512 and CR369221) we can find something regarding generics and EJB3, but is unrelated to those analyzed here http://weblogic-wonders.com/weblogic/2010/06/21/ejb3-weblogic10-3-and-generics-issue/

Appendix C: enable-bean-class-redeploy

Unlike Oracle's fast-swap, this feature already present from WebLogic 8.1 works perfectly with generics and class hierarchies, but there is not yet found a productive way to use it on Maven.