Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any way to modify the value of a `private static final` field in Java from outside the class?

I know this is normally rather stupid, but don't shoot me before reading the question. I promise I have a good reason for needing to do this :)

It's possible to modify regular private fields in java using reflection, however Java throws a security exception when trying to do the same for final fields.

I'd assume this is strictly enforced, but figured I'd ask anyway just in case someone had figured out a hack to do this.

Let's just say I have an external library with a class "SomeClass"

public class SomeClass 
{
  private static final SomeClass INSTANCE = new SomeClass()

  public static SomeClass getInstance(){ 
      return INSTANCE; 
  }

  public Object doSomething(){
    // Do some stuff here 
  }
} 

I essentially want to Monkey-Patch SomeClass so that I can execute my own version of doSomething(). Since there isn't (to my knowledge) any way to really do that in java, my only solution here is to alter the value of INSTANCE so it returns my version of the class with the modified method.

Essentially I just want to wrap the call with a security check and then call the original method.

The external library always uses getInstance() to get an instance of this class (i.e. it's a singleton).

EDIT: Just to clarify, getInstance() is called by the external library, not my code, so just subclassing won't solve the issue.

If I can't do that the only other solution I can think of is to copy-paste entire class and modify the method. This isn't ideal as I'll have to keep my fork up to date with changes to the library. If someone has something a little more maintainable I'm open to suggestions.

like image 646
James Davies Avatar asked Apr 20 '09 05:04

James Davies


2 Answers

It is possible. I've used this to monkeypatch naughty threadlocals that were preventing class unloading in webapps. You just need to use reflection to remove the final modifier, then you can modify the field.

Something like this will do the trick:

private void killThreadLocal(String klazzName, String fieldName) {
    Field field = Class.forName(klazzName).getDeclaredField(fieldName);
    field.setAccessible(true);  
    Field modifiersField = Field.class.getDeclaredField("modifiers");
    modifiersField.setAccessible(true);
    int modifiers = modifiersField.getInt(field);
    modifiers &= ~Modifier.FINAL;
    modifiersField.setInt(field, modifiers);
    field.set(null, null);
}

There is some caching as well around Field#set, so if some code has run before it might not necessarily work....

like image 73
benmmurphy Avatar answered Sep 28 '22 04:09

benmmurphy


Any AOP framework would fit your needs

It would allow you to define a runtime override for the getInstance method allowing you to return whatever class suits your need.

Jmockit uses the ASM framework internally to do the same thing.

like image 23
Jean Avatar answered Sep 28 '22 05:09

Jean