Jody Grassel created OPENJPA-2726:
-------------------------------------
Summary: Under certain conditions an Embeddable can be directly
admitted into the Datacache map
Key: OPENJPA-2726
URL: https://issues.apache.org/jira/browse/OPENJPA-2726
Project: OpenJPA
Issue Type: Bug
Components: kernel
Affects Versions: 2.2.2
Reporter: Jody Grassel
Assignee: Jody Grassel
Fix For: 2.2.3
A certain sequence of events can result in an Embeddable object instance, with
a null identity, being admitted to the DataCache. This has a number of
consequences, ranging from errors by third party datacache implementations
which cannot accept a null key value (which the native OpenJPA datacache impl
can), to possible data integrity issues since a null key can refer to any kind
of object type (since the regular key includes both
identity and identity type in its information.)
Consider the following entities:
@Entity
public class LeftHand {
@Id private long id;
private String strData;
@OneToMany private Collection<RightHand> rhList;
...
@Entity
public class RightHand {
@Id private long id;
@Basic private String strData;
@Embedded private EmbeddableData emb;
...
@Embeddable
public class EmbeddableData {
@Basic private String embeddedString;
@Basic(fetch=FetchType.LAZY) private String lazyEmbeddedString;
After committing the above entities with filled data, the contents of the L2
datacache is as follows:
this TestDataCache (id=42)
cm ConcurrentDataCache$1 (id=43)
[0] ConcurrentHashMap$Entry (id=68)
key LongId (id=73)
value DataCachePCDataImpl (id=74)
_cache "default" (id=85)
_data Object[4] (id=88)
[0] Long (id=102)
[1] AbstractPCData$ProxyDataList (id=104)
[2] "left hand" (id=110)
[3] Long (id=111)
_exp -1
_fieldImpl null
_impl null
_loaded BitSet (id=91)
_oid LongId (id=73)
_type Class<T>
(org.apache.openjpa.persistence.cache.jpa.model.LeftHand) (id=94)
_version Integer (id=99)
[1] ConcurrentHashMap$Entry (id=69)
key LongId (id=116)
value DataCachePCDataImpl (id=117)
_cache "default" (id=85)
_data Object[3] (id=120)
[0] DataCachePCDataImpl (id=125)
_cache "default" (id=85)
_data Object[2] (id=128)
[0] "Embedded String"
(id=130)
[1] "Lazy String" (id=131)
_exp -1
_fieldImpl null
_impl null
_loaded BitSet (id=129)
_oid BrokerImpl$StateManagerId
(id=3611)
_type Class<T>
(org.apache.openjpa.persistence.cache.jpa.model.EmbeddableData) (id=2336)
_version null
[1] Long (id=126)
[2] "right hand" (id=127)
_exp -1
_fieldImpl null
_impl null
_loaded BitSet (id=121)
_oid LongId (id=116)
_type Class<T>
(org.apache.openjpa.persistence.cache.jpa.model.RightHand) (id=122)
_version null
Here we see that the datacache contains two entries, one for LeftHand, one for
RightHand. Completely expected, and at this point Life is Good.
After purging the persistence context and L2 cache, a query "SELECT lh from
LeftHand lh" is executed, and iterating through its result list yields the
following L2 datacache state:
this TestDataCache (id=42)
cm ConcurrentDataCache$1 (id=43)
[0] ConcurrentHashMap$Entry (id=3657)
key LongId (id=3660)
value DataCachePCDataImpl (id=3661)
_cache "default" (id=85)
_data Object[4] (id=3662)
[0] Long (id=3665)
[1] null
[2] "left hand" (id=3666)
[3] Long (id=111)
_exp -1
_fieldImpl null
_impl null
_loaded BitSet (id=3667)
_oid LongId (id=3660)
_type Class<T>
(org.apache.openjpa.persistence.cache.jpa.model.LeftHand) (id=94)
_version Long (id=111)
[1] ConcurrentHashMap$Entry (id=3658)
key null
value DataCachePCDataImpl (id=3670)
_cache "default" (id=85)
_data Object[2] (id=3671)
[0] "Embedded String" (id=3673)
[1] "Lazy String" (id=3674)
_exp -1
_fieldImpl null
_impl null
_loaded BitSet (id=3675)
_oid null
_type Class<T>
(org.apache.openjpa.persistence.cache.jpa.model.EmbeddableData) (id=2336)
_version null
[2] ConcurrentHashMap$Entry (id=3659)
key LongId (id=3682)
value DataCachePCDataImpl (id=3683)
_cache "default" (id=85)
_data Object[3] (id=3686)
[0] DataCachePCDataImpl (id=3688)
_cache "default" (id=85)
_data Object[2] (id=3691)
[0] "Embedded String"
(id=3673)
[1] "Lazy String" (id=3674)
_exp -1
_fieldImpl null
_impl null
_loaded BitSet (id=3694)
_oid BrokerImpl$StateManagerId
(id=3695)
_type Class<T>
(org.apache.openjpa.persistence.cache.jpa.model.EmbeddableData) (id=2336)
_version null
[1] Long (id=3689)
[2] "right hand" (id=3690)
_exp -1
_fieldImpl null
_impl null
_loaded BitSet (id=3687)
_oid LongId (id=3682)
_type Class<T>
(org.apache.openjpa.persistence.cache.jpa.model.RightHand) (id=122)
_version null
A specific sequence of events, requiring the embeddable to contain a lazy
loaded field (which forces a ROPStoreManager.load() in the
AbstractPCData.toEmbeddedData() path results in the
DataCacheStoreManager.load() operation attempting to admit the embeddable into
the Datacache as if it was an entity type (object
ConcurrentHashMap$Entry(id=3658) at index [1].) The embeddable has no identity
of its own, so is inserted into the cache with a null key value. The OpenJPA
datacache impl replaces the null value with an object that represents the null
value -- while other datacache implementations attempt to add the key to a
regular map instance which results in a NullPointerException.
The attached patch looks for the attempt to insert a new (embeddable) item into
the datacache at updateDataCache() - admission into the datacache is rejected
if this condition is met. I've further modified cacheStateManager() to reject
any attempt to admit an entry into the datacache that has a null entity to
guard against other unidentified paths which could lead to a similar issue, as
a null key identity in a datacache is meaningless. A unit test has been added
to verify that the fix corrects the issue.
--
This message was sent by Atlassian JIRA
(v6.4.14#64029)