javaserializationcdijava-ee-7weld

WELD-001413: The bean... has a non-passivation-capable dependency Producer Method (CDI 1.2)


I'm trying to upgrade from CDI 1.0 to CDI 1.2 but i'm facing the following problem:

org.jboss.weld.exceptions.UnserializableDependencyException: WELD-001413: The bean Managed Bean [class ViewProcessContext] with qualifiers [@Default @Named @Any] declares a passivating scope but has a non-passivation-capable dependency Producer Method [ConfigurationReader] with qualifiers [@Default @Any] declared as [[BackedAnnotatedMethod] @Produces @Default @Singleton public ConfigurationReaderProducer.process()]
    at org.jboss.weld.bootstrap.Validator.validateInjectionPointPassivationCapable(Validator.java:442)
    at org.jboss.weld.bootstrap.Validator.validateInjectionPointForDeploymentProblems(Validator.java:380)
    at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:277)
    at org.jboss.weld.bootstrap.Validator.validateGeneralBean(Validator.java:130)
    at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:151)
    at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:494)
    at org.jboss.weld.bootstrap.ConcurrentValidator$1.doWork(ConcurrentValidator.java:64)
    at org.jboss.weld.bootstrap.ConcurrentValidator$1.doWork(ConcurrentValidator.java:62)
    at org.jboss.weld.executor.IterativeWorkerTaskFactory$1.call(IterativeWorkerTaskFactory.java:62)
    at org.jboss.weld.executor.IterativeWorkerTaskFactory$1.call(IterativeWorkerTaskFactory.java:55)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

The codes, which works fine on CDI 1.0, are the following:

Where the error happens:

@Named
@ConversationScoped
public class ViewProcessContext implements Externalizable {
//...
    @Inject
    private ConfigurationReader compReader;
//...
}

Injected dependency:

public interface ConfigurationReader extends Serializable {
}

Producer:

@ApplicationScoped
public class ConfigurationReaderProducer implements Externalizable {
//...
@Produces
    @Default
    @Singleton
    public ConfigurationReader process() {
    }
}

According to CDI spec:

A producer method is passivation capable if and only if it never returns a value which is not passivation capable at runtime.

So my producer always returns a passivation capable instance.
I can't understand why Weld complains about it.

What is invalid about the producer or the dependency in this case?


Solution

  • Hm, I could reproduce your problem. I re-read CDI 1.0 and 1.2 specs. CDI 1.2 is actually somewhat clearer than CDI 1.0 and the changings in Weld are, as far as I can see, quite correct.

    See http://docs.jboss.org/cdi/spec/1.2/cdi-spec.html#passivating_scope

    First: Validation of producer-methods:

    6.6.5.Validation of passivation capable beans and dependencies

    If a producer method declares a passivating scope and:

    • has a return type that is declared final and does not implement or extend Serializable, or,
    • has an injection point that is not passivation capable.

    6.6.1.Passivation capable beans

    A producer method is passivation capable if and only if it never returns a value which is not passivation capable at runtime.

    Conclusion: A producer method MUST always be annotated with a passivation capable annotation if you want to use the result in a passivation capable scope.

    Well, which scopes are passivation capable? Answer: Only Session and conversation scopes plus your own ones that declare @NormalScope(passivating=true). Means, @Singleton is NOT (See 6.6.4.Passivation scopes).

    You maybe can work around that problem, but:

    Do you really want the Singleton to be used in your conversationScope bean? When you conversationScoped-Bean will be passivated, your Singleton will be as well. You need to implement readResolve and writeReplace (see Serializable Api) to truly create a singleton. There will be no proxy object around it.

    Rethink your solution, in most cases an (proxied) applicationScoped-Object is what you want.

    Nevertheless, you actually can inject a @Singleton-Bean into a @ConversationScope via the standard @Inject mechanism (no producer, just plain inject). Please note, that a @Singleton-bean won't be automatically detected with a beans.xml and bean-discovery-mode="annotated" (and you need readResolve etc. as stated).

    Finally: Does it make sense that you can plain-inject a singleton but cannot via producer method? I'd say: no. But that's how it's written in the spec, I'm sorry.

    Good luck.