Aside from recompiling rt.jar
is there any way I can replace the currentTimeMillis()
call with one of my own?
1# The right way to do it is use a Clock
object and abstract time.
I know it but we'll be running code developed by an endless number of developers that have not implemented Clock
or have made an implementation of their own.
2# Use a mock tool like JMockit to mock that class.
Even though that only works with Hotspot disabled -Xint
and we have success using the code bellow it does not "persist" on external libraries. Meaning that you'd have to Mock it everywhere which, as the code is out of our control, is not feasible. All code under main()
does return 0 milis (as from the example) but a new DateTime()
will return the actual system millis.
@MockClass(realClass = System.class)
public class SystemMock extends MockUp<System> {
// returns 1970-01-01
@Mock public static long currentTimeMillis() { return 0; }
}
3# Re-declare System
on start up by using -Xbootclasspath/p
(edited)
While possible, and though you can create/alter methods, the one in question is declared as public static native long currentTimeMillis();
. You cannot change it's declaration without digging into Sun's proprietary and native code which would make this an exercise of reverse engineering and hardly a stable approach.
All recent SUN JVM crash with the following error:
EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00000, pid=4668, tid=5736
4# Use a custom ClassLoader (new test as suggested on the comments)
While trivial to replace the system CL using -Djava.system.class.loader
JVM actually loads up the custom classLoader resorting to the default classLoader and System is not even pushed trough the custom CL.
public class SimpleClassLoader extends ClassLoader {
public SimpleClassLoader(ClassLoader classLoader) {
super(classLoader);
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
return super.loadClass(name);
}
}
We can see that java.lang.System
is loaded from rt.jar
using java -verbose:class
Line 15: [Loaded java.lang.System from C:\jdk1.7.0_25\jre\lib\rt.jar]
I'm running out of options.
Is there some approach I'm missing?
You could use an AspectJ compiler/weaver to compile/weave the problematic user code, replacing the calls to java.lang.System.currentTimeMillis() with your own code. The following aspect will just do that:
As discussed in the comments, it is possible that option #3 in the original question has actually worked, successfully replacing the default
System
class.If that is true, then application code which calls
currentTimeMillis()
will be calling the replacement, as expected.Perhaps unexpectedly, core classes like
java.util.Timer
would also get the replacement!If all of the above are true, then the root cause of the crash could be the successful replacement of the
System
class.To test, you could instead replace
System
with a copy that is functionally identical to the original to see if the crashes disappear.Unfortunately, if this answer turns out to be correct, it would seem that we have a new question. :) It might go like this:
I'm not 100% sure if I oversee something here, but you can create your own
System
class like this:than import your own
System
class in every java file. Reorganize imports in Eclipse should do the trick. And than all java files should use your applicatikon specificSystem
class.As I said, not a nice solution because you will need to maintain your
System
class whenever Java changes the original one. Also you must make sure, that always your class is used.i've tried using javassist to remove the native currentTimeMills, add a pure java one and load it using bootclasspath/p, but i got the same exception access violation as you did. i believe that's probably because of the native method registerNatives that's called in the static block but it's really too much to disassemble the native library.
so, instead of changing the System.currentTimeMills, how about changing the user code? if the user code already compiled (you don't have source code), we can use tools like findbugs to identify the use of currentTimeMillis and reject the code (maybe we can even replace the call to currentTimeMills with your own implementation).