4

I am using Weblogic 12c (12.1.3 to be specific) to deploy my (EJB 3.1) application.

In my application, I have a @Stateful bean, which holds references to an EntityManager and other @Stateless beans, injected using @PersistenceContext and @EJB annotations respectively.

My issue is that when my stateful bean is being passivated and serialized to disk, Weblogic tries to serialize the references to the stateless beans as well, and throws a NotSerializableException referring to the bean's proxy that was injected by Weblogic. For comparison - the EntityManager reference is passivated and reactivated without issue at all. It is only the stateless beans who cause issues.

I know I can define @PrePassivate and @PostActivate methods to make my code work, but is there any way I can make the container handle the Stateless beans references on its own?

Attaching sample code which reproduces the problem for me.

Remote bean interface:

import javax.ejb.Remote;

@Remote
public interface Passivate {

    public void doSomething();

}

Stateful Bean implementation:

import javax.ejb.EJB;
import javax.ejb.PostActivate;
import javax.ejb.PrePassivate;
import javax.ejb.Stateful;

@Stateful(mappedName = "PassivateBean")
public class PassivateBean implements Passivate {

    @EJB(mappedName = "NoStateBean")
    private NoState noStateBean;

    @Override
    public void doSomething() {
        System.out.println("Hello world");
    }

    @PrePassivate
    public void prePassivate() {
        // as a workaround - can set noStateBean to null here
    }

    @PostActivate
    public void postActivate() {
        // as a workaround - can lookup and set noStateBean here manually
    }
}

NoState Local interface:

import javax.ejb.Local;

@Local
public interface NoState {

    public void foo();

}

NoState bean implementation:

import javax.ejb.Stateless;

@Stateless(mappedName = "NoStateBean")
public class NoStateBean implements NoState {

    @Override
    public void foo() {
        System.out.println("foo");
    }

}

And finally, the exception I am getting when PassivateBean is being passivated to disk:

<Jul 14, 2015 2:36:20 PM IDT> <Error> <EJB> <BEA-010024> <Error occurred during passivation: java.io.NotSerializableException: passivateTest.NoStateBean_i02rk_Impl
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
    at weblogic.ejb.container.utils.Serializer.writeObject(Serializer.java:52)
    at passivateTest.PassivateBean_dmn8u8_Impl.writeObject(Unknown Source)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:988)
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496)
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
    at weblogic.ejb.container.swap.PassivationUtils.write(PassivationUtils.java:84)
    at weblogic.ejb.container.swap.DiskSwap.write(DiskSwap.java:173)
    at weblogic.ejb.container.manager.StatefulSessionManager.swapOut(StatefulSessionManager.java:1246)
    at weblogic.ejb.container.cache.LRUCache.passivateNode(LRUCache.java:199)
    at weblogic.ejb.container.cache.LRUCache.timerExpired(LRUCache.java:187)
    at weblogic.timers.internal.TimerImpl.run(TimerImpl.java:304)
    at weblogic.work.SelfTuningWorkManagerImpl$WorkAdapterImpl.run(SelfTuningWorkManagerImpl.java:548)
    at weblogic.work.ExecuteThread.execute(ExecuteThread.java:311)
    at weblogic.work.ExecuteThread.run(ExecuteThread.java:263)
Dagan Sandler
  • 475
  • 1
  • 5
  • 16
  • 3
    The EJB spec says you should be able to keep references to local and remote EJBs even if they're not serializable: http://what-when-how.com/enterprise-javabeans-3-1/the-stateful-session-bean-enterprise-javabeans-3-1/ (see "The passivated state") ...at least that's the theory, but I have no idea why you're getting that error, so clearly there's more to it than that. – hugh Jul 14 '15 at 12:55
  • all fields of a statefull bean must implement Serializable. define them as transient and restore on preactivate using `Instance` injection or direct lookup – fantarama Jul 14 '15 at 13:02
  • 1
    @fantarama - as @hugh pointed out, according to ejb 3.1 (maybe before that, not sure) - `The types in this list (and their subtypes) are handled specially by the passivation mechanism. They do not need to be serializable; they will be maintained through passivation and restored automatically when the bean instance is activated.` so the only question is whether my stateless bean should be implementing something to make the container handle it differently? – Dagan Sandler Jul 14 '15 at 13:37
  • but `PassivateBean ` implements `Serializable`?? – fantarama Jul 14 '15 at 13:55
  • 1
    there is no requirement for the `Stateful` bean to implement `Serializable`. The above code is working flawlessly with passivation/reactivation if there is no reference to the `Stateless` bean. – Dagan Sandler Jul 14 '15 at 13:59
  • Why are you using an interface for a local EJB, that is completely redundant. Also using mappedName is not advisable since , if you really really need to specify the name attribute instead. See http://thegreyblog.blogspot.de/2010/09/introduction-to-ejb-30-injection-and.html – dngfng Jul 15 '15 at 08:54
  • Some of my beans are used as both remote and local beans, so the separation is required. I appreciate the references but I don't see how it solves the issue. – Dagan Sandler Jul 15 '15 at 10:23
  • Looks like just another bug in WebLogic. Report and/or upgrade. – BalusC Jul 20 '15 at 06:16

1 Answers1

1

As of now, this seems like an actual bug/limitation in Weblogic 12.1.3 so I am posting my workaround as a possible solution.

To make the Stateful bean go through passivation successfully, one needs to implement methods annotated with javax.ejb.PrePassivate and javax.ejb.PostActivate. The @PrePassivate method will make the stateless bean reference point to null, and the @PostActivate method will perform a lookup for that bean when the bean is being activated again.

@PrePassivate
public void prePassivate() {
    noStateBean = null;
}

@PostActivate
public void postActivate() {
    // The lookup string is correct assuming the ejb module is deployed within an ear. If your setup is different the JNDI lookup name may be slightly different.
    noStateBean = InitialContext.doLookup("java:module/NoStateBean!NoState");
}

If there are no comments or other answers in the next couple of weeks or so, I will mark this answer as the solution.

Dagan Sandler
  • 475
  • 1
  • 5
  • 16