persistence@glassfish.java.net

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

From: Sanjeeb Kumar Sahoo <Sanjeeb.Sahoo_at_Sun.COM>
Date: Wed, 06 Dec 2006 00:55:21 +0530

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