Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

How to Patch Your IDE to Fix an Urgent Bug

DZone's Guide to

How to Patch Your IDE to Fix an Urgent Bug

Java 8 will reach end-of-life soon after Java 11 is released, so move to modularize your libraries now. Learn how to patch your IDE in this tutorial.

· Performance Zone ·
Free Resource

Sensu is an open source monitoring event pipeline. Try it today.

Clock's ticking. JDK 11 will remove a bunch of deprecated modules through JEP 320, which includes the Java EE modules, which again includes JAXB, a dependency of many libraries, including jOOQ. Thus far, few people have upgraded to Java 9 or 10, as these aren't LTS releases. Unlike in the old days, however, people will be forced much earlier to upgrade to Java 11, because Java 8 (the free version) will reach end-of-life soon after Java 11 is released:

End of Public Updates for Oracle JDK 8
As outlined in the Oracle JDK Support Roadmap below, Oracle will not post further updates of Java SE 8 to its public download sites for commercial use after January 2019

So, we library developers must act and finally modularize our libraries. Which is, quite frankly, a pain. Not because of the module system itself, which works surprisingly well. But because of the toolchain, which is far from being production ready. This mostly includes:

It's still almost not possible to maintain a modularised project in an IDE (I've tried Eclipse and IntelliJ, not Netbeans so far) as there are still tons of bugs. Some of which are showstoppers, halting compilation in the IDE (despite compilation working in Maven). For example:

But rather than just complaining, let's complain and fix it

Let's Fix Our Own IDE by Patching It

Disclaimer: The following procedure assumes that you have the right to modify your IDE's source and binaries. To my understanding, this is the case with the EPL licensed Eclipse. It may not be the case for other IDEs.

Disclaimer 2: Note, as reddit user fubarbazqux so eloquently put it, there are cleaner ways to apply patches (and contribute them) to the Eclipse community, if you have more time. This article just displays a very easy way to do things without spending too much time to figure out how the Eclipse development processes work, internally. It shows a QUICK FIX recipe

The first bug was already discovered and fixed for Eclipse 4.8, but its RC4 version seems to have tons of other problems, so let's not upgrade to that yet. Instead, let's apply the fix that can be seen here to our own distribution. It's just a single line:

How do we do this?

First off, go to the Eclipse Packages Download page.

And download the "Eclipse IDE for Eclipse Committers" distribution:

It will contain all the Eclipse source code, which we'll need to compile the above class. In the new workspace, create a new empty plugin project:

Specify the correct execution environment (in our case Java 10) and add all the Java Development Tools (JDT) dependencies:

Or just add all the available dependencies, it doesn't really matter.

You can now open the type that you want to edit:

Now, simply copy the source code from the editor and paste it in a new class inside of your project, which you put in the same package as the original (split packages are still possible in this case, yay).

Inside of your copy, apply the desired patch and build the project. Since you already included all the dependencies, it will be easy to compile your copy of the class, and you don't have to build the entirety of Eclipse.

Now, go to your Windows Explorer or Mac OS X Finder, or Linux shell or whatever and find the compiled class:

This class can now be copied into the Eclipse plugin. How to find the appropriate Eclipse plugin? Just go to your plugin dependencies and check out the location of the class you've opened earlier:

Open that plugin from your Eclipse distribution's /plugins folder using 7zip or whatever zipping tool you prefer, and overwrite the original class file(s). You may need to close Eclipse first, before you can write to the plugin zip file. And it's always a good idea to make backup copies of the original plugin(s).

Be careful that if your class has any nested classes, you will need to copy them all like so:

MyClass.class
MyClass$1.class // Anonymous class
MyClass$Nested.class // Named, nested class


How Do I Fix My Own Bugs?

You may not always be lucky to find a bug with an existing fix in the bug tracker, as in the second case.

No problemo. We can hack our way around that as well. Launch your normal Eclipse instance (not the "Eclipse IDE for Eclipse Committers" one) with a debug agent running, by adding the following lines to your eclipse.ini file:

-Xdebug 
-Xnoagent 
-Djava.compile=NONE 
-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005

Launch Eclipse again, then connect to your Eclipse from your other "Eclipse IDE for Eclipse Committers" instance by connecting a debugger:


And start setting breakpoints wherever you need, e.g. here, in my case:

java.lang.NullPointerException
at org.eclipse.jdt.internal.compiler.problem.ProblemHandler.handle(ProblemHandler.java:145)
at org.eclipse.jdt.internal.compiler.problem.ProblemHandler.handle(ProblemHandler.java:226)
at org.eclipse.jdt.internal.compiler.problem.ProblemReporter.handle(ProblemReporter.java:2513)
at org.eclipse.jdt.internal.compiler.problem.ProblemReporter.deprecatedType(ProblemReporter.java:1831)
at org.eclipse.jdt.internal.compiler.problem.ProblemReporter.deprecatedType(ProblemReporter.java:1808)
at org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope.checkAndRecordImportBinding(CompilationUnitScope.java:960)
at org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope.faultInImports(CompilationUnitScope.java:471)
at org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope.faultInTypes(CompilationUnitScope.java:501)
at org.eclipse.jdt.internal.compiler.Compiler.process(Compiler.java:878)
at org.eclipse.jdt.internal.compiler.ProcessTaskManager.run(ProcessTaskManager.java:141)
at java.lang.Thread.run(Unknown Source)

And start analyzing the problem like your own bugs. The nice thing is, you don’t have to fix the problem, just find it, and possibly comment out some lines of code if you think they’re not really needed. In my case, luckily, the regression was introduced by a new method that is applied to JDK 9+ projects only:

String deprecatedSinceValue(Supplier<AnnotationBinding[]> annotations) {
    // ...
}

The method will check for the new @Deprecated(since="9") attribute on the @Deprecated annotation. Not an essential feature, so let's just turn it off by adding this line to the source file:

String deprecatedSinceValue(Supplier<AnnotationBinding[]> annotations) {
    if (true) return;
    // ...
}

This will effectively prevent the faulty logic from ever running. Not a fix, but a workaround. For more details about this specific issue, see the report. Of course, never forget to actually report the issue to Eclipse (or whatever your IDE is), so it can be fixed thoroughly for everyone else as well

Compile. Patch. Restart. Done!

Conclusion

Java is a cool platform. It has always been a very dynamic language at runtime, where compiled class files can be replaced by new versions at any moment, and re-loaded by the class loaders. This makes patching code by other vendors very easy, just:

  • Create a project containing the vendors' code (or if you don't have the code, the binaries)
  • Apply a fix/workaround to the Java class that is faulty (or if you don't have the code, decompile the binaries if you are allowed to)
  • Compile your own version
  • Replace the version of the class file from the vendor by yours
  • Restart

This works with all software, including IDEs. In the case of jOOQ, all our customers have the right to modification, and they get the sources as well. We know how useful it is to be able to patch someone else's code. This article shows it. Now, I can continue modularising jOOQ, and as a side product, improve the toolchain for everybody else as well.

Again, this article displayed a QUICK FIX approach (some call it "hack"). There are more thorough ways to apply patches/fixes, and contribute them back to the vendor.

Another, very interesting option would be to instrument your runtime and apply the fix only to bytecode:

Image title

And https://www.sitepoint.com/fixing-bugs-in-running-java-code-with-dynamic-attach/.

A Note on IntelliJ and NetBeans

Again, I haven't tried NetBeans yet (although I've heard its Java 9 support has been working very well for quite a while).

While IntelliJ's Jigsaw support seems more advanced than Eclipse's (still with a few flaws as well), it currently has a couple of performance issues when compiling projects like jOOQ or jOOλ. In a future blog post, I will show how to "fix" those by using a profiler, like:

  • Java Mission Control (can be used as a profiler, too)
  • YourKit
  • JProfiler

Profilers can be used to very easily track down the main source of a performance problem. I've reported a ton to Eclipse already. For instance, this one.

Where a lot of time is being spent in the processing of Task Tags, like:

The great thing about profiling this is:

  • You can report a precise bug to the vendor
  • You can find the flawed feature and turn it off as a workaround. Turning off the above task tag feature was a no-brainer. I'm not even using the feature.

So, stay tuned for another blog post, soon.

Sensu: workflow automation for monitoring. Learn more—download the whitepaper.

Topics:
performance ,tutorial ,debugging ,java 11 ,java 8 ,ide

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}