Overriding single classes from rt.jar - java

I'm looking for a neat way to override a class from the bootstrap class path, rt.jar.
The reason is OpenJDK7 bug http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7104625
The fix for this bug is a trivial (see linked mailing list post) change to sun.awt.X11.XComponentPeer. So I was wondering if there is an easy way to override just this one affected class on my classpath, without having to repack/rebuild rt.jar (so the fix isn't lost on the next automatic update of OpenJDK).
Ideally, it would also affect Eclipse...
I assume that java -Djava.system.class.loader=myClassLoader would work? Is there any other way to override a single class with such a "hotfix"? (Note: not used in my own code, but deep in Java AWT code)

You can use the VM parameter -Xbootclasspath/p to prepend your own JAR file with the patched class to the boot class path.

I believe the only supported way of doing this is to "patch" rt.jar by replacing the desired *.class file. 7-Zip can help you easily do this.
This is exactly how Oracle supplied their double-parsing bug fix with their FPUpdater tool, which was essentially a script that did just this. (Some history.)

I think you can try to use javaagent
You must intercept event, when JVM loads system class and swap it to yours

I think #ziesemer is correct, but you may be able to use the classloader to replace the offending class when your app is bootstrapping. This may be cleaner if you don't want to worry about the JDK updating underneath you, though you'd have to stick this bootstrapping classloader code into every app you are working on.

Related

Java - prevent field assignment using reflection on instantiation?

I have a java class in a 3rd party lib with a private member which is assigned at class instantiation.
public class CacheLookupUtil extends AbstractCacheLookupUtil<InvocationContext> {
#Inject
private BeanManagerUtil beanManagerUtil;
private CacheKeyGenerator defaultCacheKeyGenerator = new DefaultCacheKeyGenerator();
private CacheResolverFactory defaultCacheResolverFactory = new DefaultCacheResolverFactory();
...
...
}
My problem is that the assignment of defaultCacheResolverFactory is causing an exception due to the wrong constructor having been chosen.
If I try to subclass CacheLookupUtil, this assignment is still done in the parent class, so I'm no further ahead.
Is there any mechanism I can use in Java reflection that would allow me to construct/instantiate the object, but prevent the assignment of defaultCacheResolverFactory, and allowing me to set the value via reflection?
I know this is an ugly solution, but to be honest, I cannot visualize any other way to proceed.
Is DefaultCacheResolverFactory part of your libraries jar?
If not I would guess that this is a version problem.
Otherwise you should lookout for a bugfix version of your library or open a ticket.
Last but not least you could use AspectJ Load-Time Weaving to manipulate the bytecode at class loading time. But this requires that you always start your code with Load-Time Weaving. See Load-Time Weaving.
So I personally would prefer option 1 or 2.
Check the version of library that contains CacheLookupUtil (I understand that its a thirdparty class). For example, let it be jar-A.
Then check the version of jar that contains DefaultCacheResolverFactory. If its also jar-A, this effectively means that this library doesn't work at this version, so you should upgrade.
If its in some jar-B, then check pom.xml of jar-A itself, what version of dependency on jar-B is required, probably you override the version of this jar.
Then adjust the version so that jar-A's expectations from the version of jar-B will match :)
For me its the best solution.
Now as for dirty tricks. One trick can be to create your own copy of CacheLookupUtil and put it into the same package, depending on class-loaders policy (you haven't specified in which environment do you run, so I assume plain java) it might load first and effectively "substitute" CacheLookupUtil from the jar.
Of course the same can be done with DefaultCacheResolverFactory (so that you could fix the no-op constructor there)
If you believe its a real bug, an another option to consider is to fork from the "buggy" library and create your own version of it with a fix. Of course you better make the developers of the original library to fix this bug so that eventually you could get back to the official version, in the world of open source, sometimes solutions like this work as long as the licencing permits doing so.
If it doesn't help, then Byte Code manipulation is the only way to fix as #PowerStat already mentioned. this I believe, Java Agent, class loading patching, AspectJ, and so forth. Hopefully you won't get there only because of this issue :)

Can I modify the byte code of a Java method in the runtime?

I am writing a plugin of another large java program .
I want to modify some byte code of some java method of the java program during runtime, so that I can intercept the method calls (namely, inject some hooking code into the method).
Any way can achieve this?
PS:
I've checked the following approaches:
1.change the classloader of the java program. (we CANNOT change it)
2.use java proxy. (We CANNOT use java proxy, because java proxy would create a new proxy object. We DON'T use the proxy object. We need to hook the java program's object, and Use that object)
3. use -javaagent option ( we CANNOT add the commandline option for the java program.)
PS more [Edited again]:
My classes was loaded by ext class loader (I put my jar files in JAVA_HOME\lib\ext folder).
The large java program is an applet program loaded by Browser. When the browser start the applet, it also loads my classes.
PS more more [Edited again]:
Although it's running in Applet. I can have full permission. Because I can modify java.policy and java.security file.
Thanks,
Calvin
Just use -javaagent opiton, which is used to modify the bytecode at runtime. You can find more about -javaagent from This Link or from This Link
There are several libraries which you can use. See for example here.
Once a class was already loaded/initialized by the VM it will be impossible to manipulate, though.
By the way, in principle you can also just replace the class to be 'hooked' with your own proxy class file. As long as the class' visible interface does not change this may work. (Sub-classes of the class may horribly fail at runtime though.) This replacement can be as easy as changing the classpath so that your class of the same name will be found first, before the original one. Delegating to the original class of the same name may be a little more complex in this case.
Yes, you can, but the process would be a bit tricky, as you would operate directly with memory. For this purpose, you'd look at unofficial documentation on sun.misc package and its Unsafe class.
Warning 1: the Unsafe class would be removed in JDK 9 according to official sources.
Warning 2: the Sun company would not take responsibility for your code to work correctly, as this class should not be used at all, and exists for system usage only.
Sorry, but this is not possible. First off, bytecode is immutable after classloading. The JVM provides several APIs that can be used to do something like this, but they are obviously highly privileged.
If you're running in a low privilege environment like a browser Applet, then you're obviously not going to be allowed to do this, and any method you could should be considered a security vulnerability.
But the question is why you are using applets in the first place, and why you want to modify code after loading. There's almost certainly a better way to do what you're trying to do.

any method to change/instrument class loaded in bootclassloader in latest sun jre

needs:
we need to hack one class in plugin.jar, in detail, change one class's non static method's detail implementation. as in latest jre , -Xbootclasspath/p doesn't work and only -Xbootclasspath/a works. also we don't want to change whole jre.
We know our last solution is to change the plugin.jar file directly. But want to know if there is some better solution which don't need to change the jar file directly.
for example, don't know if things like JVMTI or JVMPI will work? OR somehow change JVM's imple in an easy way?
depends on your class, but you could probably use a javaagent to inject a custom ClassFileTransformer that loads a different class, if queried... (simple example on javaagents). But this only works, if the class was not loaded before...

How do I control which ClassLoader loads a class?

The situation at hand is not as simple as the title seems to indicate.
Java 1.6_17 running via JWS.
I have a class, lets say MyClass and one of its instance member variables is a Type from an errant 3rd party library where during class initialization it dynamically tries loading some of its own classes with Class.forName(String). In one of these cases it happens to dynamically call: Class.forName("foo/Bar").This class name doesn't follow the JLS for binary names and ultimately leads to a java.lang.NoClassDefFoundError: foo/Bar.
We have a custom ClassLoader which I've added a sanitize method to ClassLoader.findClass(String) and ClassLoader.loadClass(String) which fixes this problem.
I can call stuff like:
myCustomClassLoader.findClass("foo/Bar")
Which then loads the class without any problems. But even if I load the class ahead of time, I still get the exception later. This is because during initialization of MyClass which refers to Bar - their code ends up calling Class.forName("foo/Bar") in a static block somewhere. This actually would be OK if the ClassLoader it was trying to use was my custom class loader. But it isn't. It is the com.sun.jnlp.JNLPClassLoader which doesn't do such sanitation, thus my problem.
I've made sure that Thread.currentThread().getContextClassLoader() is set to my custom class loader. But this (as you know) has no effect. I even set it as the first thing i do in main() due to some stuff I read and still, MyClass.class.getClassLoader() - is the JNLPClassLoader. If I could force it to NOT be the JNLPClassLoader and to use mine instead, problem solved.
How can I control which ClassLoader is used to load the class via their static Class.forName("foo/Bar") call made during class initialization? I believe if I can force MyClass.class.getClassLoader() to return my custom class loader, my problem will be resolved.
I'm open to other options if anyone has ideas.
TL;DR: Help me force all Class.forName(String) calls in a third party library which are referenced by MyClass - to use the classloader of my choosing.
This reminds me of an article I read 10 years ago about the classloading arrangements in Java. It's still there on JavaWorld.
The article won't answer your question directly, but it may help understand your problem. You need to cause MyClass to be loaded through your custom class loader and trump the default class loading behavior, which is to first delegate class loading to the parent classloader and only attempt to load a class if that fails.
Allowing MyClass to get loaded by a classloader other than yours will store a relationship from the instantiated class to that classloader (via getClassLoader) and cause Java to use that other classloader to try to discover any referenced classes found at compile time, effectively bypassing your custom class loader by virtue of the class loader hierarchy and the delegation model. If MyClass is instead defined by your class loader, you get a second chance.
It sounds like a job for something like URLClassLoader, overriding loadClass and trumping the delegation model for classes residing in your JARs. You'll probably want to use a bootstrap approach (as suggested by Thomas in a comment above) to force a single entrypoint class to be loaded through your custom class loader, dragging all the others with it.
Also informative is this other JavaWorld article by the same guy, which warns you about the caveats of Class.forName. That too may trip your classloading arrangements.
I hope this helps and proves informative. In any case, it sounds like a difficult solution that is easy to break as your code evolves.
I think everyone gave good solid attempts at answering the problem. However, it turns out that I misdiagnosed the problem.
I had a coworker take over the problem and asked him to get a JDK with debug flags on so we could debug the JNLPClassLoader to see what was going on as I had tried all of the suggestions here + some.
We ended up getting OpenJDK because recompiling the JDK from scratch is a total nightmare (we tried). After getting OpenJDK working with our product and debugging through the JNLPClassLoader - it turns out that it was still using a REALLY old .jnlp from months earlier that had the resource path wrong and thus why it couldn't find the class.
We were confused why it was still using the ancient .jnlp even though we had redeployed the server correctly many times with the correct .jnlp and lots of code changes between which were reflected in our client application when run.
Well, it turns out that on client machines, Java caches the .jnlp file. Even if your application changes and it redownloads your application, it still won't re-download the new .jnlp for whatever reason. So it will use all of the new code, but look up resources/class paths using the cached .jnlp.
If you run:
javaws -uninstall
On the client machine then that will clear the .jnlp cache and next time it will use the correct .jnlp file.
Really sad that this was the problem. Hopefully, this saves someone else endless hours of frustration like it caused us.
If you run out of ideas with patching the ClassLoaders themselves, you might consider rewriting the library bytecode itself -- just replace the "foo/bar" constant with the correct value, and then you don't need to customize further class loading at all!
You could do this either at runtime or beforehand.

Java 6 and SwingUtilities2

There is component within the application that uses com.sun.java.swing.SwingUtilities2 Now I understand that this class shouldn't be used, but it's a component within the system that uses it.
Therefore since it's no longer available in Java 6 I get a NoClassDefFoundError. How can I get around this issue without having to upgrade the component as I don't yet know if that's an option.
If you have absolutely no other choice, then you should figure out exactly what it was that the class is using from SwingUtilities2, and then make proxies for that functionality in your own SwingUtilities2. You can then stick it in your own com.sun.java.swing package, which will overlap with the original one, and if the same class loader that loads your component is also aware of SwingUtilities2, then the one will see the other and your application will work.
Depending on what the component is, and what it used out of SwingUtilities2, this could be significantly harder than upgrading it or even rewriting it.
Da-dum! This is precisely why you should pay attention to those pesky warnings admonishing you not to rely upon internals of the JVM!
Just a though, I don't know if this would work.
Try pulling out the SwingUtilities2 class and put it in a patch jar, include this jar in your classpath. Hopefully this works until you can change the source.
The only correct way (out of hacking) is to ask vendor to fix and rebuild this component to Java 6. The possible working way is copy sun.swing.SU2 to com.sun...SU2 and package it into separate jar (e.g. java6fix.jar) and try to run your application. It will be fine if you add this patch jar into jvm bootclasspath. The best patch should be to create own com.sun..SU2 and delegate all calls to sun.swing.SU2. And take a look for different version of component which support Java6 maybe also from different vendor. Also if the problem is only in the mentioned line ((Boolean)c.getClientProperty(AA_TEXT_PROPERTY_KEY)); then you may put your own client property for this component to prevent NPE. When you take this path you can just simply create your own com.sun...SU2.AA_TEXT_PROPERTY_KEY and call c.setClientProperty(AA_TEXT_PROPERTY_KEY, true) on this component. Also try to disable anti aliasing check on component if possible.

Categories

Resources