18

This was asked 8 years ago here and since then 8 years have passed. I wanted to ask this question again to see if anyone has developed a framework, tool or library that does monkey patching.

Basically what I need it for is a java application that I applied my own patch to. Since this project is maintained by another team I want to be able to keep/apply any patch I make, to the patches they make.

Community
  • 1
  • 1
Grim
  • 2,398
  • 4
  • 35
  • 54
  • To others who stumble into this question because they are searching for an answer: the accepted answer here doesn't really answer the question as asked, at least at least not according to what most would term "monkey patching." The answers from G_H, eis, and myself are more direct to the question at hand. – Loduwijk Feb 09 '17 at 16:23
  • Also, given the answer that was accepted, there really is no connection to Java. I am tempted to suggest an edit to remove `java` tag, but the question as posed does still require it so I'll leave it alone. – Loduwijk Feb 09 '17 at 16:24
  • 1
    Also, to those who keep saying as Rich did " "Monkey patching" in the literal sense that is used in Ruby is still not possible in Java": I say that is half-true. As soon as you decide you want to be able to patch aspect X and you set it up to be patchable, then after that time all such pre-designated aspects are patchable. For example, to change `Math.PI` you could do it by class-loading in a different `Math` class. Granted, this is much easier said than done. To preserve everything not-PI, the new `Math` can wrap the original. It is not trivial (understatement), but is possible. – Loduwijk Feb 09 '17 at 16:34
  • @Aaron -- I think you are right, and I think we agree except perhaps on terminology. I have clarified in my question what I understand by the technical term "monkey patching" in its original Ruby sense. I have already mentioned the technique of replacing whole classes at class-load time in my answer under the heading "Per-class patching". – Rich Feb 09 '17 at 17:09
  • Given your edit, I would still say that is possible. It just requires you to anticipate and prepare beforehand for that likelihood. If you run the application in such a way as to facilitate it, then it is possible in Java. I suppose you could say that doesn't count since that is a rather high hoop to jump through, and Java/Java-apps do not support this out-of-the-box. – Loduwijk Feb 09 '17 at 17:54
  • Ex: "class DoA { void f() { stuff; } }" can be patched to do "other-stuff" instead of "stuff" by injecting "class DoA { void f() { other-stuff; } }", and this can even be done *at runtime*, while the application is live in production, as long as you have implemented a class loader that handles the change gracefully. Example, you could make a class loader which searches directory "/home/me/myApp/hotSwap" to see if there are any new class files to use, allowing you to merely drop the "monkey patch" into a directory. And that's only 1 of many ways. A very highly customized environment, I admit. – Loduwijk Feb 09 '17 at 18:01
  • See the code sample in my answer for just such a situation that does that in a naïve but very live way. It allows the class to be be patched live every 2 seconds. – Loduwijk Feb 09 '17 at 18:05

5 Answers5

21

There are a number of techniques that might be applicable here, but your question is too vague to narrow them down to a single answer.

"Monkey patching" in the literal sense that it is used in Ruby (i.e. "replace methods of a class at runtime", see e.g. [1]) is possible with "Java Agents" and the "retransform" API, but it's much harder than it is in Ruby.

Source code patching

I need it for a java application that I applied my own patch to

If there is an app for which you have the source code, say in git, then you could fork their project, apply your own patch and build a modified version.

I want to be able to keep apply any patch I make, to the patches they make.

If you create your patch on a branch then it will be easy with git to pull in any future changes from the "upstream" project into your branch, and build a new modified version.

Replacing classes at class-load time via classpath precedence

An easy technique that more closely approximates Monkey Patching is to compile a single class from the target app, with modifications, and put it earlier on your classpath than the original JAR. (This is covered in the old Monkey Patching q at this answer: https://stackoverflow.com/a/381240/8261 )

The JVM loads all classes by name, and will use the first classfile it finds on the classpath for any class, so you can replace the classes one-by-one from the project you wish to modify. If you have the source for the target project, then copy that into your app file-by-file and then apply your patches to the java source.

(You will need to manually apply any future upstream changes with this approach.)

Transforming classes at class-load time or "retransforming" method bodies at any time via JVM Agents

The JVM has an API called "Java Agents" which lets you register code to modify classes at the time they are loaded.

There is also a "retransform" API that lets you change the definition of already loaded classes. This is used by JRebel to update code in running applications. It's much more limited that Ruby's monkey patching in that you can't add or remove methods (you can change method bodies).

This mechanism is used by https://github.com/fommil/class-monkey to "monkey patch" a JVM bug, for example.

Community
  • 1
  • 1
Rich
  • 15,048
  • 2
  • 66
  • 119
  • I think source code patching is the simplest solution as I would just pull from their repo and solve merge conflicts in my fork. – Grim Feb 09 '17 at 16:15
4

"Pure" monkey patching requires a dynamic language, which Java is not. So technically it's not possible. Your best bet for making runtime changes is to do bytecode manipulation. A common library used for this type of work is ASM.

G_H
  • 11,739
  • 3
  • 38
  • 82
4

You can do almost anything if you create your own class loader.

This article about reloading classes at runtime is not exactly what you are doing but the information can be very useful for what you want to do.

And this stackoverflow question/answer about changing the default class loader would also be helpful.

By loading a different version of the class with your MonkeyPatchClassLoader custom class loader, you can have your versions of the classes do something different, or or they can delegate certain tasks to the original version of the class (that is, you could have the new version of the class encapsulate the old version of the class).

Java reflections API might also come in handy, depending on what you want to change and how you want to change it.

Here is a sample code snippet from the first link about class loading just to get you thinking in that direction:

// Every two seconds, the old User class will be dumped, a new one will be loaded and its method hobby invoked.
public static void main(String[] args) {
  for (;;) {
    Class<?> userClass = new DynamicClassLoader("target/classes")
      .load("qj.blog.classreloading.example2.ReloadingContinuously$User");
    ReflectUtil.invokeStatic("hobby", userClass);
    ThreadUtil.sleep(2000);
  }
}

To change the default class loader:

java -cp Example.jar -Djava.system.class.loader=example.ClassLoader example.ClassA

And this is a good place to start with reflection.

If this is not enough to get you to where you want, update your question with the obstacles that these tools are failing/having-difficulty overcoming, and maybe we can overcome them.

Community
  • 1
  • 1
Loduwijk
  • 1,950
  • 1
  • 16
  • 28
  • 1
    While this is clever, I don't think it's Monkey Patching, i.e. "replace methods of a class at runtime". At "runtime" can include after a class has been loaded. Once a class has been loaded once, it will not be reloaded again (except in contrived examples like that). With Ruby's Monkey Patching, I can add methods to classes without having to change any calling code. – Rich Feb 13 '17 at 14:06
2

Two options came to my mind, though there's probably plenty more that are "close", though not exactly that:

  • if you can use OSGi container, those can load, reload and unload jars on the fly and activate based on that. Describing OSGi is worth of separate discussion: see What does OSGi solve?.

  • Netflix gateway applicance Zuul uses a technique where it polls file system for filter code and takes those into use runtime. It also stores these changes into Cassandra. Internally, there's FilterLoader using DynamicCodeCompiler implementation, GroovyCompiler. So it actually compiles Groovy files for the task and then loads them into memory.

For the actual monkey patching, reflection api with all its limits is pretty much all you have. Which I think is a good thing.

Community
  • 1
  • 1
eis
  • 51,991
  • 13
  • 150
  • 199
2

Yes, you can monkey patch Java classes at runtime. You can use sun.misc.Unsafe to access private fields at runtime, then run through a class finder and and change the references to set them to whatever you want. Wrote a blog post here.

https://tersesystems.com/blog/2014/03/02/monkeypatching-java-classes/

I did reply to the original question as well, so I'm not sure why you didn't mention it. It is part of ssl-config:

https://github.com/lightbend/ssl-config/blob/master/ssl-config-core/src/main/scala/com/typesafe/sslconfig/ssl/MonkeyPatcher.scala

Will Sargent
  • 4,346
  • 1
  • 31
  • 53