import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.instrument.ClassDefinition;
import java.lang.instrument.Instrumentation;

/**
 * RedefinerAgent java agent showing an error with InstrumentationImpl.redefineClasses() when supplied
 * with bytecode read from sun.plugin2.main.client.PluginMain class.
 *
 * Reproducible on:
 * OSX version 10.7.5
 * java version " 1.7.0_21 "
 * Java(TM) SE Runtime Environment (build 1.7.0_21-b12)
 * Java HotSpot(TM) 64-Bit Server VM (build 23.21-b01, mixed mode)
 *
 * @author lanza
 *
 */
public class RedefinerAgent {

    public static void premain(String agentArguments, Instrumentation instrumentation) {
        System.out.println( "Redefiner agent init!" );
        patch( "sun.plugin2.applet.Applet2ClassLoader" , instrumentation);
        patch( "sun.plugin2.main.client.PluginMain" , instrumentation);
    }

    private static void patch(String className, Instrumentation instrumentation) {
        try {
            System.out.println( "Start patching: " + className);

            Class<?> clazz = Class.forName(className, false, ClassLoader.getSystemClassLoader());
            byte[] bytes = Util.getBytes(clazz);
            instrumentation.redefineClasses(new ClassDefinition(clazz, bytes));

            System.out.println( "End patching: " + className);
        } catch (InternalError e) {
            System.err.println( "Internal error redefining class: " + className);
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}



class Util {
    private static final int BUFFER_SIZE = 4096;

    public static byte[] getBytes(Class<?> clazz) throws IOException {
        String url = clazz.getName().replace( "." , "/" ) + ".class" ;
        InputStream in = null;
        ByteArrayOutputStream out = null;

        try  {
            in = ClassLoader.getSystemResourceAsStream(url);
            out = new ByteArrayOutputStream();
            copy(in, out);
            return out.toByteArray();
        } finally {
            if (in != null) in.close();
            if (out != null) out.close();
        }
    }

    public static void copy(InputStream in, OutputStream out) throws IOException {
        byte[] buffer = new byte[BUFFER_SIZE];
        int read;

        while ((read = in.read(buffer)) != -1) {
            out.write(buffer, 0, read);
        }
    }
}

