3

I have the following code:

public void doJob() {
    MyObj s;

    for ( Object o : MyObj.all().fetch()) {
        s = (MyObj) o;  // ClassCastException here

        if (!s.fileExists()) {
        //Do some stuff
        }
    }
}

which is throwing this exception:

play.exceptions.JavaExecutionException: models.MyObj cannot be cast to models.MyObj
    at play.jobs.Job.call(Job.java:155)
    at Invocation.Job(Play!)
Caused by: java.lang.ClassCastException: models.MyObj cannot be cast to models.MyObj
    at jobs.OrphanSurveys.doJob(OrphanSurveys.java:18)
    at play.jobs.Job.doJobWithResult(Job.java:50)
    at play.jobs.Job.call(Job.java:146)
    ... 1 more

(This method runs inside a Play Job class, if that matters.)

The MyObj.all().fetch() is returning an Iterable of some kind containing all of the MyObj objects in the database. MyObj inherits this method from the Play! Framework's Model class, if that matters. That's why it's returning a list of Objects rather than MyObjs, and I can't change how it works.

So, is there some reason that I can't cast back to MyObj? I can see how there would be some weirdness casting back from an Object, but Java seems to know what the class of the object used to be.

Thanks!

andronikus
  • 4,125
  • 2
  • 29
  • 46

3 Answers3

2

I saw a recent post here on StackOverflow that indicated that if two otherwise identical instances of the same class are loaded by different classloaders, you cannot cast between them.

Post in question

Check whether you are not subject to the multiple classloader condition here too.

Community
  • 1
  • 1
mcfinnigan
  • 11,442
  • 35
  • 28
2

It looks like you have ClassLoader issues. The objects being returned by your fetch() method were loaded in a different ClassLoader than the one being used in the current thread to try and cast.

Try this to confirm. Add the three lines of code to your exising code.

for ( Object o : MyObj.all().fetch()) {
    // Check classloaders
    System.out.println(o.getClass().getClassLoader());
    System.out.println(MyObj.class.getClassLoader());
    break;
    //
    s = (MyObj) o;  // ClassCastException here

    if (!s.fileExists()) {
    //Do some stuff
    }
}
Octavio Luna
  • 340
  • 4
  • 9
Robin
  • 24,062
  • 5
  • 49
  • 58
  • I added the lines you mentioned (except for the `break`, which was making the compiler complain about unreachable code =/). Magically, that seems to have fixed the problem, because the output is identical for both classloaders, and it's no longer complaining about the cast. I'm suspicious, of course, so I'm going to leave it running overnight and see if it happens again later. But, +1 for now :D – andronikus Sep 15 '11 at 16:19
  • 1
    Then something in your runtime must have changed since those lines of code won't affect the classloading. – Robin Sep 15 '11 at 17:12
  • That's what I was thinking, yeah. Play does some fancy dynamic compilation that may be getting tangled up. This is starting to look like a race condition, which is both exciting and terrifying. Fortunately this is not exactly a vital process. If something comes up overnight I'll post the relevant log outputs tomorrow. – andronikus Sep 15 '11 at 18:24
  • Looks like it probably was multiple classloaders. I couldn't reproduce my bug, so maybe it fixed itself somehow? If I were to legitimately have this problem, how would I go about fixing it? – andronikus Sep 16 '11 at 13:13
0

From your stack trace, apparently, there's some other kinds of entries in your collection. Use o.getClass().getName() inside your loop to know what is .all().fetch() really returning.

Note: Maybe some model.Survey objects?

Anthony Accioly
  • 21,918
  • 9
  • 70
  • 118
  • Ah, sorry about that. The actual class I'm using is called `Survey`, but I was trying to be metasyntactic by using `MyObj`. I must have missed that one! Fixed in my question. They should all be the same class. – andronikus Sep 15 '11 at 15:10
  • The "caused by" was clear that the cast failed on the same class, even before the edit. – Robin Sep 15 '11 at 17:10