7

I have two ClassLoaders which loads the same class. So, obviously these can't cast to one another. But I need to access an object created in the other ClassLoader.

I have access to both ClassLoaders. How can I use that object in the other class? I don't need to cast the object to match to the current ClassLoader.

But the issue is that the returned object's type is Object. So, I have to cast down that object to access some methods. How can I do that? Normal cast like the following causes ClassCastException, which I already know.

Mojo mojo = (Mojo) descriptor.getMojo();

descriptor#getMojo() returns an object of type Mojo but the method returns Object. How can do this?

Let me know if you need further info.

I've read all the theories about classloading, but none has specified a proper solution for this.

Paolo Forgia
  • 6,572
  • 8
  • 46
  • 58
ravana
  • 157
  • 1
  • 2
  • 9
  • In your instance, what happens if you do: `Object o = descriptor.getMojo(); System.out.println(o.getClass);` with the two different classloaders? –  Aug 16 '11 at 06:58
  • The most important question here would be: what are you *actually* trying to achieve here? Is this an academic exercise or there is a real use case backing this situation? – Sanjay T. Sharma Aug 16 '11 at 06:59
  • @Bringer128 It returns the same class name package.Mojo. I was thinking whether it's possible perform the said casting via getClass() and #cast(Object o) method? – ravana Aug 16 '11 at 07:05
  • @Sanjay T. Sharma Absolutely not. I'm working on fixing an existing code. – ravana Aug 16 '11 at 07:06
  • @ravana Have you seen [this question](http://stackoverflow.com/questions/826319/classcastexception-when-casting-to-the-same-class)? –  Aug 16 '11 at 07:50
  • @Bringer128 yes, the accepted answer's first suggestion seems to apply here. But it's really brief, and doesn't give a proper final solution – ravana Aug 16 '11 at 09:24
  • @ravana I don't have the experience to answer that so I'll leave it to someone else. –  Aug 16 '11 at 09:33

6 Answers6

8

AFAIK, no, you can't cast an object of a class loaded by one class-loader in another class loader.

  • One solution would be to create a "common" class-loader which loads the classes to be used by your custom classloaders. So in your case, you'd have a new classloader which would load the given class and your custom classloaders would extend this classloader.
  • Another solution would be to pass around the "serialized" state between the two classloaders. Serialize one instance to a byte array and reconstruct the object in the other classloader by de-serializing the object stream.
Sanjay T. Sharma
  • 22,857
  • 4
  • 59
  • 71
  • I don't want to cast it from one class-loader to another. But that's what happens here when I do the said casting. How can I do this cast down by within the same classloader? (Mojo) descriptor.getMojo() just doesn't work! – ravana Aug 16 '11 at 07:11
  • This is because the "Mojo" instance inside the descriptor was loaded by a different classloader than the one you are trying to retrieve it in. I need more context here. What kind of an application is this? Web application? Are multiple threads involved here? – Sanjay T. Sharma Aug 16 '11 at 07:22
  • It's a java app running on a single thread. using classworlds 1.1 for classloading. – ravana Aug 16 '11 at 07:56
1

I think better option to just store byte array instead of object. While deserliazing, get byte array back and convert into object.

I had the same issue and byte array approach worked.

ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutput out = null;
    try {
        out = new ObjectOutputStream(bos);
        out.writeObject(cachedValue);
        byte b[] = bos.toByteArray();

        //Store in DB, file wherever here using b[]. I am not writing code to store it as it may vary in your requirement.

    } catch (IOException e) {
        e.printStackTrace();
    }

Read from byte array:

ByteArrayInputStream bis = new ByteArrayInputStream(<<read byte[] where you stored earlier>>);
    ObjectInput in = null;

    try {
        in = new ObjectInputStream(bis);
        <Your Class >cachedRes = ( Your Class) in.readObject();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
1

Reflection isn't that bad, and is appropriate here.
Is this a Maven plugin, BTW?

You'll want something like:

Mojo mojo = (Mojo)descriptor.getClass().getMethod("getMojo").invoke(descriptor);

I'm leaving out a lot - particularly exception handling - but this should lead you to the Javadoc you need. It's quite good, but read carefully.

If you also have two Mojo classes, the cast will break, and you'll have to do more reflection to do whatever you need to do with the evil-twin Mojo.

Ed Staub
  • 15,480
  • 3
  • 61
  • 91
0

I solved this issue by loading the fully qualified class using Thread's parent context loader.

As an example, using AbstractMojo here.

       Thread
      .currentThread
      .getContextClassLoader
      .getParent
      .loadClass("org.apache.maven.plugin.AbstractMojo")
0

Why you have 2 CloassLoaders, which loads the same class? This could be a programatic issue. It sounds like you are caching ClassLoader somewhere and re-use them in a wrong way. If this is not the case try a MultiClassLoader.

Create a MultiClassLoader which includes multiple other classloader. These MultiClassLoader you can use to load all Classes you wish. But you have to create these MultiClassLoader at the very beginning and not when the classes are loaded.

public class MultiClassLoader extends ClassLoader

You would have a collection of classloaders and in the findClass(...) you iterate over all these registered loaders.

protected Class findClass(String aName) throws ClassNotFoundException {
   for (Iterator iter = multiLoaders.iterator(); iter.hasNext();) {
      ClassLoader tmpLoader = (ClassLoader)iter.next();
      try {
         return tmpLoader.loadClass(aName);
      } catch (ClassNotFoundException e) {
      }
   }
   throw new ClassNotFoundException(aName);
}
Markus Lausberg
  • 12,177
  • 6
  • 40
  • 66
  • unfortunately I don't have the ability to change the class loaders. I have access to both, but I can't create something like MultipleClassloader! – ravana Aug 16 '11 at 07:01
  • Does the getClass() method returns a class that contain both the class and it's classloader? If so, is there a way to do this using a mechanism like that? – ravana Aug 16 '11 at 07:01
  • I've been looking at this solution. I could use it in another way. I'd like to know one more detail. Say, I've got the Class object via your findClass method. Then what I should do? As I've stated before, I need to cast my object to this returned class. Mojo = class.cast(object) gives me compiler errors because it thinks the class#cast returns an object of type Object. – ravana Aug 19 '11 at 12:28
  • You can implement your code like you do it normally, with a single class loader. The multiclassloader just handles multilpe classloader, thats all. – Markus Lausberg Aug 22 '11 at 06:24
0

The easiest way is to use reflection. This allow you to dó anything you Can dó in "normal" code.

Thorbjørn Ravn Andersen
  • 73,784
  • 33
  • 194
  • 347