MappedField::setFieldValue failure due to entity-cache

71 views
Skip to first unread message

green

unread,
Dec 4, 2010, 8:43:35 PM12/4/10
to morphia
I am trying to update play-morphia (http://www.playframework.org/modules/morphia) to morphia-0.98. Always found random error (more than 80% odds in my use case) with an IllegalArugmentException in MappedField::setFieldValue method due the the type of the value class is not the type of the field.

In the end I found the root cause is in the resolveObject method of ReferenceMapper class:

Object resolveObject(final DBRef dbRef, final MappedField mf, EntityCache cache, Mapper mapr) {
Key key = new Key(mf.getSubClass(), dbRef.getId());
// key.updateKind(mapper);
Object cached = cache.getEntity(key);
if (cached != null)
return cached;

The assumption of the above logic is each object in mongodb should have a unique ID. If the ID type is ObjectID, that's no problem. However, in my case, the ID type is Long, and the ID is generated as per collection name. Thus, when I expect class A object with ID = 1, the code return a class B object with ID = 1. 

I am not sure this should be attributed to a bug or a new feature that will break functionality of existing application. Please advise.

Regards,
Green

Scott Hernandez

unread,
Dec 4, 2010, 8:52:56 PM12/4/10
to mor...@googlegroups.com
On Sat, Dec 4, 2010 at 5:43 PM, green <green...@gmail.com> wrote:
> I am trying to update play-morphia
> (http://www.playframework.org/modules/morphia) to morphia-0.98. Always found
> random error (more than 80% odds in my use case) with an
> IllegalArugmentException in MappedField::setFieldValue method due the the
> type of the value class is not the type of the field.

Do you have a stack-trace? What version were you upgrading from?

> In the end I found the root cause is in the resolveObject method of
> ReferenceMapper class:
> Object resolveObject(final DBRef dbRef, final MappedField mf, EntityCache
> cache, Mapper mapr) {
> Key key = new Key(mf.getSubClass(), dbRef.getId());
> // key.updateKind(mapper);
> Object cached = cache.getEntity(key);
> if (cached != null)
> return cached;
> The assumption of the above logic is each object in mongodb should have a
> unique ID. If the ID type is ObjectID, that's no problem. However, in my

A Key<T> is a collectionName(Class) + @Id field value (_id), and has
nothing to do with an ObjectId. Now that I think about it, there is a
problem with this *if*, and only if, you are using capped collections
and repeat _id values. I'll add an issue to address that.

> case, the ID type is Long, and the ID is generated as per collection name.
> Thus, when I expect class A object with ID = 1, the code return a class B
> object with ID = 1.
> I am not sure this should be attributed to a bug or a new feature that will
> break functionality of existing application. Please advise.

If you have a test for what is going wrong please file an issue and I
can take a look at exactly what is happening.

> Regards,
> Green

green

unread,
Dec 5, 2010, 1:13:32 AM12/5/10
to mor...@googlegroups.com
I've noticed mf.getSubClass() returns null where mf is MappedField, because "subType" field of mf is null. And then there is only id information in "key". Meaning the cache is using only _id instead of collectionName + _id to index the cached object. However the realType point to the correct type information of this field.

BTW, my version is 0.98, I am upgrading from 0.96.

Here is the stacktrace:

Execution exception (In /app/Bootstrap.java around line 22)
RuntimeException occured : java.lang.IllegalArgumentException: Can not set models.User field models.Post.author to models.Post

play.exceptions.JavaExecutionException: java.lang.IllegalArgumentException: Can not set models.User field models.Post.author to models.Post
        at play.jobs.Job.call(Job.java:119)
        at Invocation.Job(Play!)
Caused by: java.lang.RuntimeException: java.lang.IllegalArgumentException: Can not set models.User field models.Post.author to models.Post
        at com.google.code.morphia.mapping.Mapper.fromDb(Mapper.java:413)
        at com.google.code.morphia.mapping.Mapper.fromDBObject(Mapper.java:265)
        at com.google.code.morphia.query.MorphiaIterator.processItem(MorphiaIterator.java:53)
        at com.google.code.morphia.query.MorphiaIterator.next(MorphiaIterator.java:48)
        at com.google.code.morphia.query.QueryImpl.get(QueryImpl.java:340)
        at Bootstrap.doJob(Bootstrap.java:22)
        at play.jobs.Job.doJobWithResult(Job.java:37)
        at play.jobs.Job.call(Job.java:110)
        ... 1 more
Caused by: java.lang.IllegalArgumentException: Can not set models.User field models.Post.author to models.Post
        at com.google.code.morphia.mapping.MappedField.setFieldValue(MappedField.java:410)
        at com.google.code.morphia.mapping.ReferenceMapper.readSingle(ReferenceMapper.java:171)
        at com.google.code.morphia.mapping.ReferenceMapper.fromDBObject(ReferenceMapper.java:145)
        at com.google.code.morphia.mapping.Mapper.readMappedField(Mapper.java:431)
        at com.google.code.morphia.mapping.Mapper.fromDb(Mapper.java:410)
        ... 8 more

Scott Hernandez

unread,
Dec 5, 2010, 1:41:41 AM12/5/10
to mor...@googlegroups.com
On Sat, Dec 4, 2010 at 10:13 PM, green <green...@gmail.com> wrote:
> I've noticed mf.getSubClass() returns null where mf is MappedField, because
> "subType" field of mf is null. And then there is only id information in

The subType/Class is only used when it is mapping a
Array/Collection/List/Map. I would assume the author of a post is not
a list, but single item, correct?

> "key". Meaning the cache is using only _id instead of collectionName + _id

The cache is a Map<Key<T>, Object>, and the Key<T> always contains
both the collection and _id value, so that a single entity can be
identified.

> to index the cached object. However the realType point to the correct type
> information of this field.

Can you provide the class def for "models.Post"? Also, can you post
the data stored in mongodb for the post that is causing the problem?

green

unread,
Dec 5, 2010, 2:13:08 AM12/5/10
to mor...@googlegroups.com
Pos.author is a referenced field. The calling chain to subType is:

ReferenceMapper.readSingle (line 168) -> ReferenceMapper.resolveObject (line 266) -> MappedField->getSubClass() (line 363)

Attached is Post, User and their parent class file.
Post.java
User.java
Model.java

Scott Hernandez

unread,
Dec 5, 2010, 2:18:20 AM12/5/10
to mor...@googlegroups.com
Ah, that sounds like a bug. I'll try to reproduce that, and fix it.

Scott Hernandez

unread,
Dec 6, 2010, 2:18:01 PM12/6/10
to mor...@googlegroups.com
I've pushed this into the latest 0.99-snapshot. Give that a try to see
if it works; there are now tests to make sure that both the collection
(class) and id value exist for each Key<T>; an exception is now thrown
for Key.compareTo/equals if a Key has somehow become invalid.

On Sat, Dec 4, 2010 at 11:18 PM, Scott Hernandez

green

unread,
Dec 6, 2010, 2:47:23 PM12/6/10
to mor...@googlegroups.com
Hi this solved the issue. Thanks for your prompt response!

Scott Hernandez

unread,
Dec 6, 2010, 2:59:41 PM12/6/10
to mor...@googlegroups.com
We aim to please :)

I'm sorry for the bug; that was dumb.

Reply all
Reply to author
Forward
0 new messages