Frequently there is confusion about passivation of contextual instances with CDI, especially when non-passivating scopes such as request scoped are injected in passivatable once.

The specification defines passivation (serialization) capabilities, allowing the container to free memory by transferring idle objects to secondary storage when required.

The temporary transfer of the state of an idle object held in memory to some form of secondary storage is called passivation.

The transfer of the passivated state back into memory is called activation.



CDI-Spec 1.2 – Chapter 6.6.1



CDI’s session and conversation scopes are the only build-in passivation capable scopes. Request and application scopes are not passivating capable. If a bean is defined with a passivating scope, it and all applied interceptors and decorators must be serializable (see CDI 1.2 Specification – 6.6.1. Passivation capable beans).

Passivation of Normal Scoped injections

When working with passivation capable scopes such as @SessionScoped, it is still possible to inject references to non-passivating scopes such as @ApplicationScoped. Application scoped beans are not passivation capable, however the injected dependencies are.

@SessionScoped public class MySession implements Serializable { @Inject private MyAppData myAppData; ... } @ApplicationScoped public class MyAppData { ... }

Even so the application scoped bean MyAppData defines a non-passivation capable scope and is not serializable, whereas the client-proxy CDI injects is. When the container passivates the session scoped instance, the injected client-proxies are serialized along with it.

When a method is invoked on the client-proxy, a contextual lookup is performed via the BeanManager, which manages creation and access to all contextual beans.

Beware of transient

Most static code analysis tools, such as FindBugs, will raise false warnings when encountering injections of non-serializable managed beans into passivation capable beans. If myAppScoped was marked transient, as suggested…

public class MySession implents Serializable { @Inject private transient NonSerializableAppScope myAppScoped; ... }

…the client proxy will not be serialized nor re-injected during deserialization and remains null. This will cause a NullPointerException on the next invocation.

Dependant beans

Relaying on the client-proxy works for all normal scopes. Pseudo scopes beans are injected as direct references. So when a pseudo scoped bean such as @Dependent, is injected into a bean with a passivating scope it will need to be serializable or transient.

public class MySession implements Serializable { @Inject private MyAppScope myAppScope; @Inject private MyDependent myDependent; ... } @Dependent public class MyDependent implements Serializable { ... }

If MyDependent is not Serializable then the myDependent will have to be marked transient. Making the dependent beans inject point transient is not advisable since it will require manual handling during deserialization.

CDI performs passivation validations on all beans defining passivating scopes prior to deployment.

For every bean which declares a passivating scope, the container must validate that the bean truly is passivation capable and that, in addition, its dependencies are passivation capable. If a managed bean which declares a passivating scope, a stateful session bean which declares a passivating scope, or a built-in bean: is not passivation capable,

has an injection point that is not passivation capable,

has an interceptor or decorator that is not passivation capable

has an interceptor or decorator with an injection point that is not passivation capable then the container automatically detects the problem and treats it as a deployment problem.



CDI-Spec 1.2, Chapter 6.6.5



When injecting a non-serializable dependent scoped bean into a bean with a passivating scope…

@SessionScoped public class MySession implements Serializable { ... @Inject private MyUnserializable mu; ... } @Dependent public class MyUnserializable { ... }

…the container detects this at start-up.

The specification does not define as to how the implementation should handle the deployment problem;

Weld throws a UnserializableDependencyException,

Caused by: org.jboss.weld.exceptions.UnserializableDependencyException: WELD-001413: The bean Managed Bean [class com.knitelius.MySession] with qualifiers [@Any @Default] declares a passivating scope but has a non-passivation-capable dependency Managed Bean [class com.knitelius.MyUnserializable] with qualifiers [@Any @Default] at org.jboss.weld.bootstrap.Validator.validateInjectionPointPassivationCapable(Validator.java:479) at org.jboss.weld.bootstrap.Validator.validateInjectionPointForDeploymentProblems(Validator.java:397)

whilst OpenWebBeans throws a WebBeansConfigurationException.

Caused by: org.apache.webbeans.exception.WebBeansConfigurationException: Passivation capable beans must satisfy passivation capable dependencies. Bean : 1113977599,Name:mySession,WebBeans Type:MANAGED,API Types:[java.io.Serializable,java.lang.Object,com.knitelius.MySession],Qualifiers:[javax.enterprise.inject.Any,javax.enterprise.inject.Default,javax.inject.Named] does not satisfy. at org.apache.webbeans.component.AbstractOwbBean.validatePassivationDependencies(AbstractOwbBean.java:702) at org.apache.webbeans.component.AbstractInjectionTargetBean.validatePassivationDependencies(AbstractInjectionTargetBean.java:596) at org.apache.webbeans.config.BeansDeployer.checkPassivationScope(BeansDeployer.java:730) at org.apache.webbeans.config.BeansDeployer.validate(BeansDeployer.java:382)

This is usually a sign of bad design and therefore either the unserializable bean should be:

assigned a non-passivating scope such as @RequestScoped,

its part of the contextual domain model and should be seralizeable

or its a utility class which should only be used as a method local variable and not as a member.

For the other 0.01% of cases it is possible to implement java.io.Serializable readObject to perform custom handling during deseralization .

... @Inject private BeanManager beanManager; @Inject private transient MyUnserializable mu; private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); Bean<MyUnserializable> bean = (Bean<MyUnserializable>) beanManager.resolve(beanManager.getBeans(MyUnserializable.class)); this.mu = (MyUnserializable) beanManager.getReference(bean, bean.getBeanClass(), beanManager.createCreationalContext(bean)) } ...

The a new instance of dependent instance of MyUnserializable can be obtained during deseralization from the BeanManager.

It is important to note that the CDI container, as per specification, is only required to validate injection points. All other members are ignored by the container.

@SessionScoped public class MySession implements Serializable { ... private UnserializableObject uso = new UnserializableObject(); ... }

Even though MySession passes the CDI deployment validation, it will fail serialization at runtime due to its non-transient unserializable member causing a java.io.NotSerializableException. All unmanaged members have to be either serializable or transient.

Constructor and Initializer method parameter injection

Non-serializable beans that are only required for bean initialization can be injected into constructor or intializer methods with the @TransientReference annotation.

@SessionScoped public class MySession implements Serializable { @Inject public MySession(@TransientReference NonSerializableDependent nsd) { ... } ... @Inject public void initalize(@TransientReference NonSerializableDependent nsd) { ... } ... }

These @TransientReference dependent scoped beans will be be destroyed at the end of the method invocation.

… all @Dependent scoped contextual instances injected into method or constructor parameters that are annotated with @TransientReference are destroyed when the invocation completes, …



CDI-Spec 1.2, Chapter 6.4.2



Wrap up