Issue Details (XML | Word | Printable)

Key: GLASSFISH-635
Type: Bug Bug
Status: Resolved Resolved
Resolution: Fixed
Priority: Major Major
Assignee: mf125085
Reporter: isana
Votes: 0
Watchers: 0
Operations

If you were logged in you would be able to see more operations.
glassfish

Toplink's VersionLockingPolicy became to throw a NullPointerException

Created: 04/May/06 01:07 AM   Updated: 30/Nov/10 05:16 PM   Resolved: 13/Jun/06 02:27 PM
Component/s: entity-persistence
Affects Version/s: 9.0pe
Fix Version/s: 9.0pe_dev

Time Tracking:
Not Specified

File Attachments: 1. Java Archive File issue635.jar (6 kB) 11/May/06 11:07 AM - mf125085

Environment:

Issuezilla Id: 635
Tags:
Participants: isana, marina vatkina and mf125085


 Description  « Hide

From (at latest) B46, Toplink's VersionLockingPolicy class became to throw a
NullPointerException in a certain point of my application with (probablly)
EntityManager.flush().

The application codes worked with B36 and not modified since it worked
(persistence.xml is modified to suite spec changes between B36 and B46). I
recognized this problem with B46, and B47 still has this problem. This issue
prevents my application from working and I don't find a workaround for this
yet. The stacktrace is as follows:
======
Caused by: java.lang.NullPointerException
at
oracle.toplink.essentials.descriptors.VersionLockingPolicy.compareWriteLockValue
s(VersionLockingPolicy.java:185)
at
oracle.toplink.essentials.internal.sessions.ObjectChangeSet.mergeObjectChanges
(ObjectChangeSet.java:562)
at
oracle.toplink.essentials.internal.sessions.UnitOfWorkChangeSet.mergeObjectChang
es(UnitOfWorkChangeSet.java:377)
at
oracle.toplink.essentials.internal.sessions.UnitOfWorkChangeSet.mergeUnitOfWorkC
hangeSet(UnitOfWorkChangeSet.java:397)
at
oracle.toplink.essentials.internal.ejb.cmp3.base.RepeatableWriteUnitOfWork.write
Changes(RepeatableWriteUnitOfWork.java:255)
at oracle.toplink.essentials.internal.ejb.cmp3.base.EntityManagerImpl.flush
(EntityManagerImpl.java:268)
at oracle.toplink.essentials.internal.ejb.cmp3.base.EJBQueryImpl.performFlush
(EJBQueryImpl.java:682)
at
oracle.toplink.essentials.internal.ejb.cmp3.base.EJBQueryImpl.executeReadQuery
(EJBQueryImpl.java:333)
at oracle.toplink.essentials.internal.ejb.cmp3.base.EJBQueryImpl.getResultList
(EJBQueryImpl.java:430)
at my.package.control.MySessionBeanImpl.findFooByDate
(MySessionBeanImpl.java:701)
at my.package.control.MySessionBeanImpl.myBusinessLogic
(MySessionBeanImpl.java:600)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke
(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at com.sun.enterprise.security.application.EJBSecurityManager.runMethod
(EJBSecurityManager.java:1050)
at com.sun.enterprise.security.SecurityUtil.invoke(SecurityUtil.java:165)
at com.sun.ejb.containers.BaseContainer.invokeTargetBeanMethod
(BaseContainer.java:2766)
at com.sun.ejb.containers.BaseContainer.intercept(BaseContainer.java:3847)
at com.sun.ejb.containers.EJBObjectInvocationHandler.invoke
(EJBObjectInvocationHandler.java:190)
... 17 more
======

I encountered the same exception in another place in my application with B44.
This is disappeared since B46, without modification with my application's codes.
======
Caused by: java.lang.NullPointerException
at
oracle.toplink.essentials.descriptors.VersionLockingPolicy.compareWriteLockValue
s(VersionLockingPolicy.java:185)
at
oracle.toplink.essentials.internal.sessions.ObjectChangeSet.mergeObjectChanges
(ObjectChangeSet.java:562)
at
oracle.toplink.essentials.internal.sessions.UnitOfWorkChangeSet.mergeObjectChang
es(UnitOfWorkChangeSet.java:377)
at
oracle.toplink.essentials.internal.sessions.UnitOfWorkChangeSet.mergeUnitOfWorkC
hangeSet(UnitOfWorkChangeSet.java:397)
at
oracle.toplink.essentials.internal.ejb.cmp3.base.RepeatableWriteUnitOfWork.write
Changes(RepeatableWriteUnitOfWork.java:255)
at
oracle.toplink.essentials.internal.ejb.cmp3.base.EntityManagerImpl.flush
(EntityManagerImpl.java:268)
at com.sun.enterprise.util.EntityManagerWrapper.flush
(EntityManagerWrapper.java:509)
at my.package.control.EntityManagerWrapper.flush
(EntityManagerWrapper.java:129)
at
my.package.control.JournalRecordManager.updateQuarterlyBalance
(JournalRecordManager.java:401)
at my.package.control.JournalRecordManager.onEpisodeModified
(JournalRecordManager.java:63)
at my.package.control.EditProgramControlImpl.createEpisode
(EditProgramControlImpl.java:283)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke
(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke
(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at
com.sun.enterprise.security.application.EJBSecurityManager.runMethod
(EJBSecurityManager.java:1050)
at com.sun.enterprise.security.SecurityUtil.invoke
(SecurityUtil.java:165)
at com.sun.ejb.containers.BaseContainer.invokeTargetBeanMethod
(BaseContainer.java:2766)
at com.sun.ejb.containers.BaseContainer.intercept
(BaseContainer.java:3847)
at com.sun.ejb.containers.EJBObjectInvocationHandler.invoke
(EJBObjectInvocationHandler.java:190)
... 17 more
======

See the GlassFish forum for additional information :
http://forums.java.net/jive/thread.jspa?threadID=15041&tstart=15



marina vatkina added a comment - 04/May/06 01:44 PM

We need more information to be able to reproduce the problem. Our tests with
version column all pass.
Do you have parallel transactions?
What kind of changes does EM try to flush?
Do you call em.lock() at any time in that or a parallel transaction?

-marina


isana added a comment - 04/May/06 03:51 PM

Question-A : Do you have parallel transactions?

No. There is only one transaction, because it's in stand-alone development
environment. No other client than GlassFish access to the DB (Oracle).

Question-B : What kind of changes does EM try to flush?

1. Create a new instance of an @Entity class. Call this instance 'hemi'.
2. Link 'hemi' to another persistent entity (which is surely attached to the
EM).
3. Make 'hemi' persistent with em.persist().
4. Find other persistent entities with several different EJB-QLs, and link them
to 'hemi'.

The code is like the followings:
===
public void someBusinessMethodInSessionBean() {
...
Quarter q = em.find(Quarter.class, quarterId);
em.refresh(q);
...
Hemi hemi = new Hemi();
hemi.setQuarter(q); // This method maintains a bi-directional relation
between Hemi and Quarter.
hemi.setStringValue("String Value");
em.persist(hemi);
addHemi(hemi, findBarBySomeCriteria(...));
addHemi(hemi, findFooByCriteria1(...));
addHemi(hemi, findFooByDate(...)); // This time, exception was thrown here.
addHemi(hemi, findFooByCriteria2(...);
addHemi(hemi, findAfoByCriteria(...);
addHemi(hemi findAafByCriteria(...);
em.flush();
} // the end of the business method.

private <T extends SomeInterface> addHemi(final Hemi hemi, final List<T>
targets) {
for (SomeInterface target : targets) { target.addHemi(hemi); // This maintains bi-directional relation. }
}

private List<Foo> findFooByDate(...) {
String ejbql = "select o from Foo o where ....";
EntityManager em = getEntityManager(); // Returns the EntityManager injected
to the bean.
Query q = em.createQuery(ejbql);
q.setParameter("...", ...);
...
return q.getResultList(); // This time, this line caused the exception.
}
===

Question-C : Do you call em.lock() at any time in that or a parallel
transaction?

No. My application never uses em.lock().

Now, I have two things to add.

I said above that there was no concurrent transaction. But, I've encountered a
strange behavior which seems to imply that there is something accessing to the
persistence context concurrently. Since yesterday when I moved into B47, the
following code suddenly became to throw
java.lang.ConcurrentModificationException. The exception is not always thrown.
But it's not so rare case.
===
public class ProgramDTO {
public ProgramDTO(Program entity) { // Program is annotated with @Entity
setId(entity.getId());
...
for (Foo foo : entity.getFooList()) { // fooList : List<Foo> fooDTO = new FooDTO(foo); this.fooDTOList.add(fooDTO); }
}
}
===
As you see, the code inside of the for-loop does not modify the entity.fooList.
The exception had never been thrown until yesterday.

And, I guess that you should run tests inside of GlassFish. As I posted to the
issue #123, Toplink's behavior was different between inside and outside of
GlassFish. I don't test outside of GlassFish these days, so I'm not sure
whether the difference still remains or not.

Thank you.

  • Ryosuke.

mf125085 added a comment - 08/May/06 03:34 PM

Assigning to me.


mf125085 added a comment - 08/May/06 03:35 PM

Starting.


mf125085 added a comment - 08/May/06 05:42 PM

Hi Ryosuke,

I suspect the problem is associated to the entities returned by the query:

findFooByCriteria1(...)

You said in your forum post

"Talking about class Foo, which was the select target of the EJB query that
caused the exception, there was only one instance. It had been made persistent
before current transaction began."

1. If there's only one Foo instance, why there are there two queries for Foo:
findFooByCriteria1(...) and findFooByDate(...)

2. Can you print out the versions of the Foo instance(-s) returned by
findFooByCriteria1(...)?

Thanks,

– markus.


isana added a comment - 08/May/06 06:24 PM

Hello, Markus

Now I answer one of your question which can be told without tests.

Question-1. If there's only one Foo instance,
why there are there two queries for Foo: findFooByCriteria1(...)
and findFooByDate(...)

The number of Foo instances was one, when I posted the first report. But it's
not limited to one. There will be usually hundreds or thousands of Foo. I
said “there is only one instance� to tell you that the situation (object
graph, etc) inside the persistence context was not so complicated but rather
simple when I got the exception. But it might lead you confusion. I’m sorry
for that.

I think that the exception was thrown when there were several Foo instances,
but I'm not sure now. I will check it later and report.

Best regards,

  • Ryosuke.

mf125085 added a comment - 10/May/06 02:46 PM

Hi Ryosuke,

in your forum post you mentioned that

"Version properties of all the @Entity classes are declared as follows:

@Version
@Column(nullable = false)
private Long objectVersion;"

As a workaround for the NPE, change all Version properties from Objects to
primitives, i.e.


@Version
@Column(nullable = false)
private long objectVersion;


mf125085 added a comment - 11/May/06 11:07 AM

Created an attachment (id=273)
testcase, unjar under entity-persistence-tests


mf125085 added a comment - 11/May/06 11:16 AM

To run the test, please unjar issue635.jar under entity-persistence-tests. Then
run the newly created

oracle.toplink.essentials.testing.tests.cmp3.advanced.EntityMappingsAdvancedJUnitTestCase

Junit test. The situation is:

An Employee is persisted, and in the same transaction two previously created
Projects are added to the Employee. The NullPointerException happens at the
second commit.


mf125085 added a comment - 11/May/06 11:30 AM

Be aware that issue635.jar contains Employee and Project classes also used by
other entity-persistence tests. I changed the version field in above classes to
be of type Integer.


mf125085 added a comment - 09/Jun/06 02:19 PM

For instances having an Object typed version field, e.g. Integer, a
NullPointerException might be thrown when comparing versions in
ObjectChangeSet#compareWriteLockValues.
Added a new method setting the initialWriteLockValue in ObjectChangeSet, which
is called when an object is first inserted to the database in
VersionLockingPolicy#setupWriteFieldsForInsert. This makes sure, that the value
initialWriteLockValue is always set, thereby preventing the NullPointerException.

The fix is checked in into the trunk:
http://fisheye5.cenqua.com/changelog/glassfish/?cs=MAIN:mf125085:20060609190609


mf125085 added a comment - 13/Jun/06 02:27 PM

Updated target milestone.