persistence@glassfish.java.net

Re: [Issue 1081] Remote business method of Session Bean returning JPA entity with lazy field

From: Gordon Yorke <gordon.yorke_at_oracle.com>
Date: Tue, 05 Dec 2006 18:40:08 -0500
Hello Sahoo,
     I agree that this is a valuable usecase but is limited by the current wording of the specification.   Perhaps Mike or Linda can voice the intentions of the spec here but I do not believe the serialization specifics were intended to apply only to when the serialized objects would be used for merging.  Without a mechanism for the user to tell the Persistence Provider about the target environment the Persistence Provider has to assume that the entities are being serialized to the vendor's environment in order to be compliant with the sections of the specification mentioned below. Including API for the user to use to notify the Persistence Provider of the target environment is a viable option that has been mentioned before but would be outside other specification an in order to proceed on this issue this is the approach we should take.
--Gordon

Sanjeeb Kumar Sahoo wrote:
Hi,

This is a fairly important issue and please excuse me for the rather long email. Since we could not reach at a conclusion on this issue during our phone conversation, we agreed to start an email discussion. For the benefits of those who were not part of our phone discussion, here is a brief back ground:

Use case:
------------
There is a remote business interface called Facade which returns a JPA entity type object City in a business method. The remote interface and the entity class definition is shown here:
@Remote
public interface Facade {
    City getCityById(int cityId);
}

@Entity
public class City implements Serializable {
    private int id;
    private Region region;

    @Id @GeneratedValue(strategy = GenerationType.AUTO)
    public int getId() {...}
    public void setId(int id) {...}

    @ManyToOne(fetch = FetchType.LAZY)
    public Region getRegion() {...}
    public void setRegion(Region region) {...}
    ...
}

The ear file is packaged like this:
ear
   lib/common.jar (contains City.class, Region.class, Facade.class.)
   ejb.jar (contains FacadeBean.class and persistence.xml)
   appclient.jar (contains Client.class)

  
As you can see from the above packaging structure, the persistence unit (PU) is scoped to the ejb module and client is using the entity classes as POJOs. For the client, they represent data transfer objects (DTOs) while accessing the session bean's remote business interface.

The bug: (https://glassfish.dev.java.net/issues/show_bug.cgi?id=1081)
-----------
When client calls the business method of the bean, it gets an exception during unmarshaling. The exception message reads something like this:
Caused by: java.io.IOException: Mismatched serialization UIDs : Source

  
Root cause:
---------------
The above exception is caused by the fact that TopLink Essential, in order to support lazy loading, has enhanced the classes in the EJB container in a such way that the client can't deserialize them using the original class even though the appclient container has TopLink Essential classes in it's classpath.

I do feel that it is a supported portable use case, but there were two other kinds of opinions, viz:
1) It is a supported use case, but appclient container should somehow enhance the class so that it can be deserialized.
2) It is not a supported use case, client must define persistence unit so that class can be enhanced.

As you can see, there is a subtle difference between #1 & #2. Even though #1 makes it transparent, it requires the client to have access to the same persistence provider runtime as the server. Is this a reasonable expectation? Shouldn't a JBoss appclient be allowed to call an EJB deployed in GlassFish without having to require GlassFish classes in JBoss client container?

#2 requires client to have access to persistence.xml. If I remember correctly, the primary reason to require persistence.xml was that container can assume JPA annotated entity classes being used as client view classes in a Java EE environment in the absence of persistence.xml.

Here is what I find in the spec:
1. section #2.1:
If an entity instance is to be passed by value as a detached object (e.g., through a remote interface), the entity class must implement the Serializable interface.

2. section #3.2.4.2 (Detached Entities and Lazy Loading):
Serializing entities and merging those entities back into a persistence context may not be interoperable across vendors when lazy properties or fields and/or relationships are used.
A vendor is required to support the serialization and subsequent deserialization and merging of detached entity instances (which may contain lazy properties or fields and/or relationships that have not been fetched) back into a separate JVM instance of that vendor's runtime, where both runtime instances have access to the entity classes and any required vendor persistence implementation classes.
When interoperability across vendors is required, the application must not use lazy loading.

Please note that the above section clearly talks about merging the detached object in a separate JVM, which is not attempted by the client in our example. It just calls the session bean to get the entity and it has no intention to access the lazy fields.

IMHO, requiring that JPA classes that do not have lazy attributes can only be portably used in remote interface will be a serious limitation. It throws away the big promise that JPA entity classes can be used as DTOs because in reality many entities have lazy attributes. I always thought the spec even allowed the client to access the lazy fields as long as it is populated by the server. It is a contract between the server and client developer. If the lazy fields have not been populated by the server, client can't access them.

Thanks,
Sahoo