JBoss marshalling fail if java.util.concurrent.PriorityBlockingQueue is used in custom objects which need to be serialized in EAP
Environment
- Red Hat JBoss Enterprise Application Platform (EAP)
- 6.4
- 7.0
Issue
java.util.concurrent.PriorityBlockingQueueimplements Serializable from the API but if object should be serialized for an EAP application it throws a NullPointerException with the cause like below.
ERROR [stderr] (EJB default - 2) Caused by: java.lang.NullPointerException
ERROR [stderr] (EJB default - 2) at java.util.concurrent.PriorityBlockingQueue.readObject(PriorityBlockingQueue.java:943)
ERROR [stderr] (EJB default - 2) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
ERROR [stderr] (EJB default - 2) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
ERROR [stderr] (EJB default - 2) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
ERROR [stderr] (EJB default - 2) at java.lang.reflect.Method.invoke(Method.java:498)
ERROR [stderr] (EJB default - 2) at org.jboss.marshalling.reflect.SerializableClass.callReadObject(SerializableClass.java:307)
Resolution
The Bug "BMAR-195 - SerializingCloner.clone of PriorityBlockingQueue causes NullPointerException" is tracked for EAP6 and EAP 7 will be fixed in future releases.
Apply the latest cumulative patch. Fixed in EAP 6.4 CP16 or later and EAP 7.0 CP8 or later
Root Cause
This content is not included.6.4 BZ 1445480
Content from issues.jboss.org is not included.7.0 JBEAP-10536
There is something unusual in how java.util.concurrent.PriorityBlockingQueue serialization works, it is implementing read/writeObject and the end of writeObject() it is setting the field q to null. This seems a bug in JBoss Marshalling where it was calling prepareFields twice, q was set the first time prepareFields was called but then the second time PriorityBlockingQueue had set it to null thus causing the NPE.
- SerializingCloner.clone called on PriorityBlockingQueue
- PriorityBlockingQueue writeObject is putting a value into q and then invoking defaultWriteObject which I think should write the field q value
- it then sets
qback to null - then back to SerializingCloner.clone it is cloning cloneFields where possibly it is setting
qto null?
Which would mean the fieldqis not set when readObject is invoked on PriorityBlockingQueue resulting in the NPE
The related code look like followed:
SerializingCloner.clone
if (origInfo.hasWriteObject()) {
final Queue<Step> steps = new ArrayDeque<Step>();
final StepObjectOutputStream stepObjectOutputStream = createStepObjectOutputStream(orig, fields, steps);
origInfo.callWriteObject(orig, stepObjectOutputStream);
stepObjectOutputStream.flush();
stepObjectOutputStream.doFinish();
prepareFields(orig, fields);
cloneFields(fields);
if (cloneInfo.hasReadObject()) {
cloneInfo.callReadObject(clone, createStepObjectInputStream(clone, cloneInfo, fields, steps));
} else {
storeFields(cloneInfo, clone, fields);
}
PriorityBlockingQueue:
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
lock.lock();
try {
// avoid zero capacity argument
q = new PriorityQueue<E>(Math.max(size, 1), comparator);
q.addAll(this);
s.defaultWriteObject();
} finally {
q = null;
lock.unlock();
}
}
This solution is part of Red Hat’s fast-track publication program, providing a huge library of solutions that Red Hat engineers have created while supporting our customers. To give you the knowledge you need the instant it becomes available, these articles may be presented in a raw and unedited form.