Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to do monkey patching in Java, if not is there an alternative?

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.

like image 304
Grim Avatar asked Feb 09 '17 14:02

Grim


People also ask

Is monkey patching a good idea?

Monkey patching is good for testing or mocking out behavior. They can be localized in factory/class decorators/metaclasses where they create a patched new class/object from another object to help with "cross-cutting concerns" in between ALL methods like logging/memoization/caching/database/persistance/unit conversion.

Is monkey patching metaprogramming?

Monkey patching is the first step towards meta-programming - writing code which writes code.

What is monkey patching in Java?

Monkey-patching is a term that refers to modifying a class or module at a run time. In simple words, a class or module's work can be changed at the runtime.

What is monkey patching and how is it used in Python?

In Python, the term monkey patch refers to dynamic (or run-time) modifications of a class or module. In Python, we can actually change the behavior of code at run-time. We use above module (monk) in below code and change behavior of func() at run-time by assigning different value.

What is monkey patching in Java?

What is Monkey patching? Monkey patching is a technique to add, modify, or suppress the default behavior of a piece of code at runtime without changing its original source code. It has been extensively used in the past by libraries, such as MooTools, and developers to add methods that were missing in JavaScript.

Should I use monkey to patch a JavaScript built-in object?

We’ve also seen a case where Monkey patching a JavaScript built-in object is a good choice: polyfills. Using Monkey patching in conjunction with a defensive technique to polyfill a method that is already part of the ECMAScript standard but it isn’t supported by some browsers is a great way to avoid creating branches in your code.

Is monkey patching good or bad?

There are very rare cases where monkey patching may be the only solution. That is why, there are so few situations where monkey patching is required, that it is considered evil by developers. Patches made to a module might not work after the module is updated and some methods are changed.

Why is monkey patching considered evil by developers?

That is why, there are so few situations where monkey patching is required, that it is considered evil by developers. Patches made to a module might not work after the module is updated and some methods are changed. This might create a major bug or cause the website/app to crash depending on the patch applied and the changes made to the module.


2 Answers

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.

like image 147
Rich Avatar answered Oct 14 '22 15:10

Rich


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.

like image 26
Loduwijk Avatar answered Oct 14 '22 13:10

Loduwijk