views:

678

answers:

10

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.

A: 

I will preface this answer by acknowledging that this is not actually an answer to your stated question about modifying a private static final field. However, in the specific example code mentioned above, I can in fact make it so that you can override doSomething(). What you can do is to take advantage of the fact that getInstance() is a public method and subclass:

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

   public SomeClass getInstance() {
        return INSTANCE;
   }

   public Object doSomething() {
      //Override behavior here!
   }
}

Now just invoke MySomeClass.getInstance() instead of SomeClass.getInstance() and you're good to go. Of course, this only works if you're the one invoking getInstance() and not some other part of the unmodifiable stuff you're working with.

CaptainAwesomePants
Unfortunately this isn't the case, getInstance is invoked entirely by the internal code of the library.
Zoomzoom83
Ah, sorry then :(
CaptainAwesomePants
+1  A: 

If you really must (though for our problem I'd suggest you use the solution of CaptainAwesomePants) you could have a look at JMockIt. Although this is intented to be used in unit tests if allows you to redefine arbitrary methods. This is done by modifying the bytecode at runtime.

Ridcully
A: 

You should be able to change it with JNI... not sure if that is an option for you.

EDIT: it is possible, but not a good idea.

http://java.sun.com/docs/books/jni/html/pitfalls.html

10.9 Violating Access Control Rules

The JNI does not enforce class, field, and method access control restrictions that can be expressed at the Java programming language level through the use of modifiers such as private and final. It is possible to write native code to access or modify fields of an object even though doing so at the Java programming language level would lead to an IllegalAccessException. JNI's permissiveness was a conscious design decision, given that native code can access and modify any memory location in the heap anyway.

Native code that bypasses source-language-level access checks may have undesirable effects on program execution. For example, an inconsistency may be created if a native method modifies a final field after a just-in-time (JIT) compiler has inlined accesses to the field. Similarly, native methods should not modify immutable objects such as fields in instances of java.lang.String or java.lang.Integer. Doing so may lead to breakage of invariants in the Java platform implementation.

TofuBeer
Can you elaborate? I thought JNI was only used to call external (i.e. C) libraries.
Zoomzoom83
+4  A: 

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.

Jean
Good answer. The key is not to modify the field, but to intercept the call to retrieve it.
skaffman
So this would esssentially be using the same bytecode rewriting trick that JMockit does?
Zoomzoom83
I am not exactly an expert but there are at least two ways to do it. One using java 1.5 instrumentation API the other using ASM and direct bytecode manipulation
Jean
A: 

If there is no external hack available (at least I am not aware of) I would have hacked the class itself. Change the code by adding the security check you want. As such its an external library, you won't be taking the updates regularly, also not many update happens anyway. Whenever that happens I can happily re-do it as it is not a big task anyway.

Bhushan
+1  A: 

You can try the following. Note: It is not at all thread safe and this doesn't work for constant primitives known at compile time (as they are inlined by the compiler)

Field field = SomeClass.class.getDeclareField("INSTANCE");
field.setAccessible(true); // what security. ;)
field.set(null, newValue);
Peter Lawrey
Unfortunately the Security Manager doesn't like this- even with setAccessible(true) it doesn't like modifying final values.
Zoomzoom83
You could try setting System.setSecurityManager(null) and see if you can disable it temporarily. ;)
Peter Lawrey
+5  A: 

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....

drscroogemcduck
Maybe if you ran this thread early in the application it would solve the caching issue?
Sam Barnum
caching issue is not really an issue. i think it will only happen if another method has attempted to set the field using reflection.
drscroogemcduck
Thanks, this is exactly what I was looking for (Wish I could upvote you more, this is nice and obscure).
Zoomzoom83
A: 

Here, your problem is good-old Dependency Injection (aka Inversion of Control). Your goal should be to inject your implementation of SomeClass instead of monkeypatching it. And yes, this approach requires some changes to your existing design but for the right reasons (name your favorite design principle here) - especially the same object should not be responsible for both creating and using other objects.

I assume the way you're using SomeClass looks somewhat like this:

public class OtherClass {
  public void doEverything() {
    SomeClass sc = SomeClass.geInstance();
    Object o = sc.doSomething();

    // some more stuff here...
  }
}

Instead, what you should do is first create your class that implements the same interface or extends SomeClass and then pass that instance to doEverything() so your class becomes agnostic to implementation of SomeClass. In this case the code that calls doEverything is responsible for passing in the correct implementation - whether be the actual SomeClass or your monkeypatched MySomeClass.

public class MySomeClass() extends SomeClass {
  public Object doSomething() {
    // your monkeypatched implementation goes here
  }
}

public class OtherClass {
  public void doEveryting(SomeClass sc) {
    Object o = sc.doSomething();

    // some more stuff here...
  }
}
Cem Catikkas
Thanks, but as mentioned in the question this won't work because I'm not calling SomeClass directly. I want to change the instance of SomeClass used by an external library- I never call it directly from my code.
Zoomzoom83
A: 

with mockito is very simple:

import static org.mockito.Mockito.*;

public class SomeClass {

    private static final SomeClass INSTANCE = new SomeClass();

    public static SomeClass getInstance() {
        return INSTANCE;
    }

    public Object doSomething() {
        return "done!";
    }

    public static void main(String[] args) {
        SomeClass someClass = mock(SomeClass.getInstance().getClass());
        when(someClass.doSomething()).thenReturn("something changed!");
        System.out.println(someClass.doSomething());
    }
}

this code prints "something changed!"; you can easily replace your singleton instances. My 0.02$ cents.

dfa