Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best practice for serialization for EJB and CDI beans

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?
like image 967
kostja Avatar asked Dec 04 '12 10:12

kostja


People also ask

How do you serialize a bean?

This can be accomplished by using another object that holds the state of the CDI bean. This other object is not managed by CDI, i.e. created with new , and is serializable. Each CDI bean that needs to persist its state has the pair of setState(state) / getState() methods - they could even be part of an interface.

What is CDI and EJB?

They are CDI, EJB3 and JSF Managed Beans. CDI is the new kid on the block. CDI beans feature dependency injection , scoping and an event bus . CDI beans are the most flexible with respect to injection and scoping. The event bus is very lightweight and very well suited for even the simplest of web applications.

What are different methods in serialization?

Serialization in Java is a mechanism of writing the state of an object into a byte-stream. It is mainly used in Hibernate, RMI, JPA, EJB and JMS technologies. The reverse operation of serialization is called deserialization where byte-stream is converted into an object.

What is a CDI bean?

A CDI bean is a POJO, plain old java object, that has been automatically instantiated by the CDI container, and is injected into all, and any qualifying injection points in the application. The CDI container initiates the bean discovery process during deployment.


3 Answers

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.

like image 128
MarkL Avatar answered Sep 20 '22 04:09

MarkL


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.

like image 34
Martin Andersson Avatar answered Sep 21 '22 04:09

Martin Andersson


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.

like image 37
LightGuard Avatar answered Sep 21 '22 04:09

LightGuard