23

I have not yet experienced any serialization-related issues. But PMD and Findbugs detect a bunch of potential problems regarding seriazation. A typical case is an injected logger that is being detected as non-serializable. but there are many more - EntityManager and several CDI beans.

I have not found any best practices on how to deal with serialization correctly.

  • will the fields, injected by @Inject and @PersistenceContext be reinjected on deserialization?
  • should they be marked as transient?
  • or should I just ignore/switch off the code checks?
  • should I really provide accessors to all those fields as PMD advises?
kostja
  • 60,521
  • 48
  • 179
  • 224

3 Answers3

27

I realize this is an old question, but I believe the only answer provided is incorrect.

will the fields, injected by @Inject and @PersistenceContext be reinjected on deserialization?

No, they will not. I personally experienced this with JBoss in a clustered environment. If the bean is passivation capable, then the container must inject a serializable proxy. That proxy gets serialized and deserialized. Once deserialized, it will locate the proper injection and rewire it. However, if you mark the field transient, the proxy is not serialized and you will see NPEs when the injected resource is accessed.

It should be noted that the injected resource or bean does not have to be Serializable, because the proxy will be. The only exception is for @Dependent scoped beans which have to be serializable or the injection transient. This is because a proxy is not used in this case.

should they be marked as transient?

No, see above.

or should I just ignore/switch off the code checks?

This is up to you, but it is what I would do.

should I really provide accessors to all those fields as PMD advises?

No, I would not. In our projects, we disable this check when we know we are using CDI.

MarkL
  • 477
  • 4
  • 11
  • Gavin King [wrote](https://community.jboss.org/message/657500#657500) injected EJBs or Resources should/must not be marked as transient as you say. But what about objects like a logger or a ResourceBundle? Weld [forces me to mark them transient](http://stackoverflow.com/questions/3000170/how-to-inject-a-non-serializable-class-like-java-util-resourcebundle-with-weld) or I'll get an IllegalProductException. If I got you right, this would leed to NPEs if marked transient. So what would be the correct way for this? – Martin Höller Nov 20 '13 at 11:32
  • Did you try request scoping your ResourceBundle producer? It's dependent on the FacesContext so it probably should be request scoped anyhow. The reason you are likely being warned about the serialization is because it looks to be @Dependent scoped (the default) and therefore must meet all the serialization requirements. – MarkL Nov 23 '13 at 02:17
  • Oh and on the Logger, I personally got away from injected loggers. You would run into a problem if they are not serializable yet wanted them to be Dependent scoped. – MarkL Nov 23 '13 at 02:21
  • Your suggestions would probably work for the mentioned examples. But these are just workarounds for the actual problem. I could still want to inject a 3rd-party non-serializable object. Anyone knows What the supposed way to deal with this situation is? – Martin Höller Nov 25 '13 at 08:09
  • And why can't you try to RequestScope the producer method for your library? This should result in a proxy that is serializable. i.e. create a producer class that has a reference to the non-serializable instance, and put the @RequestScope on the producer method that returns the value. That should result in a serilizable proxy being injected. – MarkL Nov 27 '13 at 17:17
  • I tried but I cannot change the scope to RequestScope. Weld gives me an exception because the ResourceBundle class contains final methods that cannot be overwritten by the proxy that weld would install. – Martin Höller Dec 03 '13 at 08:31
  • Ah, I see that is a problem with ResourceBundle. Nothing you can do then. For resources like these, why not just use them directly? Like the logger, I probably would avoid injections in this case. Sorry for the delayed answer too, still can't figure out how to get stackoverflow to email me when a comment is posted. – MarkL Dec 28 '13 at 07:29
  • Another option would be the delegate pattern, but seems pointless in this case. – MarkL Dec 28 '13 at 07:29
  • One more comment: these are not work arounds for the problem if the problem is indeed non-serializable objects. It is a solution for that problem. The problem you have now is simply that the implementation you are trying to proxy has final methods. – MarkL Dec 28 '13 at 07:38
8

This answer will detail the serialization/passivation semantics for EJB 3.2 (JSR 345), JPA 2.1 (JSR 338) and CDI 1.2 (JSR 346). Noteworthy is that the Java EE 7 umbrella specification (JSR 342), the Managed Beans 1.0 specification (JSR 316) and the Commons Annotations specification 1.2 (JSR 250) does not have anything to say that is of interest to us in regards to serialization/passivation.

I will also touch on the topic of static code analyzers.

EJB

Relevant sections are "4.2 Conversational State of a Stateful Session Bean" and "4.2.1 Instance Passivation and Conversational State".

@Stateless and @Singleton instances are never passivated.

@Stateful instances may be passivated. Since EJB 3.2, the class developer can opt-out from passivation using @Stateful(passivationCapable=false).

The EJB specification explicitly notes that references to things such as UserTransaction, EntityManagerFactory and container-managed EntityManager are taken care of by the container. A @Stateful instance which uses an extended persistence context will not be passivated unless all entities in the persistence context and the EntityManager implementation is serializable.

Please note that an application-managed EntityManager always uses an extended persistence context. Also, a @Stateful instance is the only type of EJB session instance which may use a container-managed EntityManager with an extended persistence context. This persistence context would be bound to the life cycle of the @Stateful instance instead of one single JTA transaction.

The EJB specification does not explicitly address what happens to a container-managed EntityManager with an extended persistence context. My understanding is this: If there is an extended persistence context, then this guy must be deemed serializable or not according to the rules defined previously and if it is, then passivation proceeds. If passivation proceeds, then the @Stateful class developer need only concern himself with references to application-managed entity managers.

The EJB specification does not specify what happens to transient fields other than describing an assumption we as developers should make.

Section 4.2.1 says:

The Bean Provider must assume that the content of transient fields may be lost between the PrePassivate and PostActivate notifications.

[...]

While the container is not required to use the Serialization protocol for the Java programming language to store the state of a passivated session instance, it must achieve the equivalent result. The one exception is that containers are not required to reset the value of transient fields during activation. Declaring the session bean's fields as transient is, in general, discouraged.

Requiring the container to "achieve the equivalent result" as Javas serialization protocol at the same time leaving it totally unspecified as to what happens with transient fields is quite sad, to be honest. The take-home lesson is that nothing should be marked transient. For fields that the container can not handle, use @PrePassivate to write a null and @PostActivate to restore.

JPA

The word "passivation" does not occur in the JPA specification. Nor does JPA define serialization semantics for types such as EntityManagerFactory, EntityManager, Query and Parameter. The only sentence in the specification relevant to us is this (section "6.9 Query Execution"):

CriteriaQuery, CriteriaUpdate, and CriteriaDelete objects must be serializable.

CDI

Section "6.6.4. Passivating scopes" define a passivating scope as a scope explicitly annotated @NormalScope(passivating=true). This property defaults to false.

One implication is that @Dependent - which is a pseudo scope - is not a passivation capable scope. Also noteworthy is that javax.faces.view.ViewScoped is not a passivation capable scope which for whatever reason the majority of Internet seems to believe. For example, section "17-2. Developing a JSF Application" in the book "Java 9 Recipes: A Problem-Solution Approach".

A passivation capable scope requires that instances of classes declared "with the scope are passivation capable" (section "6.6.4. Passivating scopes"). Section "6.6.1. Passivation capable beans" define such an object instance simply as one being transferable to secondary storage. Special class- annotations or interfaces are not an explicit requirement.

Instances of EJB:s @Stateless and @Singleton are not "passivation capable beans". @Stateful may be (stateful is the only EJB session type which it makes sense to let CDI manage the life cycle of - i.e., never put a CDI scope on a @Stateless or @Singleton). Other "managed beans" are only "passivation capable beans" if they and their interceptors and decorators are all serializable.

Not being defined as a "passivation capable bean" does not mean that things such as stateless, singleton, EntityManagerFactory, EntityManager, Event and BeanManager can not be used as dependencies inside a passivation capable instance that you author. These things are instead defined as "passivation capable dependencies" (see section "6.6.3. Passivation capable dependencies" and "3.8. Additional built-in beans").

CDI make these depedencies passivation capable through the use of passivation capable proxies (see last bulleted item in section "5.4. Client proxies" and section "7.3.6. Lifecycle of resources"). Please note that for Java EE resources such as the EntityManagerFactory and EntityManager to be passivation capable, they must be declared as a CDI producer field (section "3.7.1. Declaring a resource"), they do not support any other scope than @Dependent (see section "3.7. Resources") and they must be looked up on the client-side using @Inject.

Other @Dependent instances - albeit not declared with a normal scope and not required to be fronted by a CDI "client proxy" - can also be used as a passivation capable dependency if the instance is transferable to secondary storage, i.e., serializable. This guy will be serialized together with the client (see last bulleted item in section "5.4. Client proxies").

To be perfectly clear and to provide a few examples; a @Stateless instance, a reference to an EntityManager produced by CDI and a serializable @Dependent instance can all be used as instance fields inside your class annotated with a passivation capable scope.

Static code analyzers

Static code analyzers are stupid. I think that for senior developers, they are more a cause of concern than being an aide. False flags raised by these analyzers for suspected serialization/passivation problems is certainly of very limited value because CDI requires the container to validate that the instance "truly is passivation capable and that, in addition, its dependencies are passivation capable" or otherwise "throw a subclass of javax.enterprise.inject.spi.DeploymentException" (section "6.6.5. Validation of passivation capable beans and dependencies" and "2.9. Problems detected automatically by the container").

Finally, as others have pointed out, it is worth repeating: we should probably never mark a field as transient.

Martin Andersson
  • 18,072
  • 9
  • 87
  • 115
  • 1
    `Static code analyzers are stupid.` Absolutely not. Anyone who thinks that is implying they have no idea how to read them. Java static analysis is oversimplisitc because Java is such a safe language to develop in, which ends up translating to a lack of a market for anyone to develop a decent static analysis tool. Static analysis is far from 'stupid', and just because you don't understand what it's telling you doesn't enforce that opinion. Findbugs is a free tool - you pay for what you get. It doesn't know the EJB spec, and I wouldn't expect it to. That doesn't make all static analysis stupid. – searchengine27 Jan 22 '19 at 20:52
1

PMD and FindBugs are only checking the interfaces and also have no information about the environment in which your code will be running. To quiet the tools, you could mark them as transient, but they'll all be properly re-injected upon deserialization and first use regardless of the transient keyword.

LightGuard
  • 5,298
  • 19
  • 19
  • Thank you, LightGuard. It sounds relieving :) Could you be so kind to add a reference to support this - I have searched quite a bit, but was unable to find anything unambiguous. – kostja Dec 05 '12 at 07:05
  • If I had one. My thoughts would be the spec itself (especially version 1.1 that is now in public draft) or the Weld documentation. – LightGuard Dec 05 '12 at 14:12