views:

295

answers:

4

If one has serialized the entire .class file into byte[], and assuming the name of the class is known (passed along with the byte[]), how do you convert byte[] -> Class -> then load it to the JVM so that I could later use it by calling the Class.forName()?

NOTE: I'm doing this because I sent the .class over to another host, and the host's JVM doesn't know about this .class.

A: 

Extend ClassLoader and then implement the necessary call to obtain your byte[] inside the method defineClass(). Conversely you can do this in findClass(). You'll thus load this ClassLoader at the beginning or employ it when you need class definitions via your array.

public class ByteArrayClassLoader extends ClassLoader {

    public Class findClass(String name) {
     byte[] ba = /* go obtain your byte array by the name */;

     return defineClass(name,ba,0,ba.length);
    }

}
Xepoch
dont you also need to call resolveClass()?
ShaChris23
+2  A: 

Use the defineClass(String, byte[], int, int) method from ClassLoader

Matt
A: 

Okay, so here's what I did:

public class AgentClassLoader extends ClassLoader
{
  public void loadThisClass(ClassByte classByte_)
  {
    resolveClass(defineClass(classByte_.getName(),
                             classByte_.getBytes(),
                             0,
                             classByte_.getBytes().length));
  }
}

I hope that loads the class into JVM? If this really works, why didn't Java implementator make both defineClass and resolveClass public methods? I really wonder what they were thinking. Anyone can enlighten me?

Just for completeness, here's ClassByte

import java.io.Serializable;

public class ClassByte implements Serializable
{
  private String name;
  private byte[] bytes;

  ClassByte(String name_, byte[] bytes_)
  {
    name  = name_;
    bytes = bytes_;
  }

  public String getName()
  {
    return name;
  }

  public byte[] getBytes()
  {
    return bytes;
  }
}
ShaChris23
I think that the idea is to discourage random code from calling `defineClass` on the system/default class loaders. This has the potential to compromise security and generally make **really strange** things happen. It is better to only allow this on custom classloaders.
Stephen C
+3  A: 

I'm actually using something like this right now in a test to give a set of Class definitions as byte[] to a ClassLoader:

  public static class ByteClassLoader extends URLClassLoader {
    private final Map<String, byte[]> extraClassDefs;

    public ByteClassLoader(URL[] urls, ClassLoader parent, Map<String, byte[]> extraClassDefs) {
      super(urls, parent);
      this.extraClassDefs = new HashMap<String, byte[]>(extraClassDefs);
    }

    @Override
    protected Class<?> findClass(final String name) throws ClassNotFoundException {
      byte[] classBytes = this.extraClassDefs.remove(name);
      if (classBytes != null) {
        return defineClass(name, classBytes, 0, classBytes.length); 
      }
      return super.findClass(name);
    }

  }
Alex Miller
Alex, but what if 2 .class are dependent on one another? would it still work?
ShaChris23
Probably not. You should probably be sending (for example) a JAR file not a sequence of separate .class files.
Stephen C
@ShaChris23 - sure, why wouldn't it? @Stephen C - that seems like a limited view of what Java classloaders can do. Why do you say probably not?
Alex Miller