Prateek Agrawal
Prateek Agrawal

Reputation: 31

I want to change java class at run time which is already loaded. how to do this?

First time class is created successfully but next time when there is change in class (like add some variables) It throws an error. Below is my code.

ClassPool pool = ClassPool.getDefault();
        CtClass cc=null;
        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        if (contextClassLoader != null)
        {
            pool.insertClassPath(new LoaderClassPath(contextClassLoader));
        }
        try{

            cc = pool.makeClass(className); 
            cc.defrost();
            for (Entry<String, Class<?>> entry : properties.entrySet()) {
                cc.addField(new CtField(resolveCtClass(entry.getValue()), entry.getKey(), cc));
                // add getter
                cc.addMethod(generateGetter(cc, entry.getKey(), entry.getValue()));
                // add setter
                cc.addMethod(generateSetter(cc, entry.getKey(), entry.getValue()));

            }
            cc.addConstructor(generateConstructor(cc,properties,className));
            CtConstructor defaultCons=new CtConstructor(NO_ARGS, cc);
            defaultCons.setBody(";");
            cc.addConstructor(defaultCons);
            return cc.toClass();
        }catch(Exception e){
            cc = pool.get(className);   
            cc.detach();
            cc = pool.makeClass(className); 
            cc.defrost();
            for (Entry<String, Class<?>> entry : properties.entrySet()) {
                cc.addField(new CtField(resolveCtClass(entry.getValue()), entry.getKey(), cc));
                // add getter
                cc.addMethod(generateGetter(cc, entry.getKey(), entry.getValue()));
                // add setter
                cc.addMethod(generateSetter(cc, entry.getKey(), entry.getValue()));

            }
            cc.addConstructor(generateConstructor(cc,properties,className));
            CtConstructor defaultCons=new CtConstructor(NO_ARGS, cc);
            defaultCons.setBody(";");
            cc.addConstructor(defaultCons);
            return **cc.toClass();** // getting error at this line
        }

Upvotes: 2

Views: 4990

Answers (2)

Rafael Winterhalter
Rafael Winterhalter

Reputation: 44032

You need to write a Java agent and attach it dynamically to transform a class after it was already loaded. A Java agent defines a method named agentmain which takes an instance of Instrumentation as its second argument. With such a Java agent, the changes you are allowed to apply are however limited to changing the body of a method, you cannot add members (fields or methods) or change the signature of existing ones.

You call Instrumentation::redefineClass to apply a redefinition.

You can find articles on how to change loaded code on several places online.

Upvotes: 4

GhostCat
GhostCat

Reputation: 140447

Simple answer: you can't (easily).

The point is: when the class is already loaded then the JVM has already information about that class. The "default" classloader simply does not allow you to replace that definition with a new one.

If you want to dynamically exchange class definitions, you have to turn to the advanced topic of writing your own classloader that allows for such things. See here as starting point.

Upvotes: 1

Related Questions