17

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?

Frankie
  • 24,627
  • 10
  • 79
  • 121
  • AspectJ might be an option. – chrylis -cautiouslyoptimistic- Aug 14 '13 at 18:50
  • Out of flags, but possible duplicate of http://stackoverflow.com/questions/2001671/override-java-system-currenttimemillis-for-testing-time-sensitive-code . – Jason C Aug 14 '13 at 18:51
  • Can you change the calls to `System.currentTimeMillis()`? If so, using [Joda Time](http://joda-time.sourceforge.net/) might be an option. [DateTimeUtils](http://joda-time.sourceforge.net/apidocs/org/joda/time/DateTimeUtils.html) has methods to set a time offset - see a similar question [here](http://stackoverflow.com/questions/5622194/time-dependent-unit-tests). – andersschuller Aug 14 '13 at 18:52
  • @JasonC Imagine a simulated mode where the inputs will be faster that real time. If it helps think of a weather station that will report the passage of time very fast. – Frankie Aug 14 '13 at 18:53
  • @andersschuller the code this will run is developed by *other developers* so no, I can't use Joda. They may be using Joda. Joda relies on System.currentTimeMillis() so replacing it would mean Joda would keep working. – Frankie Aug 14 '13 at 18:54
  • 1
    Well, I suppose you could always use CGLIB and just return your own value using their method interceptor. – Josh M Aug 14 '13 at 18:55
  • @JasonC the main difference between my question and the one you refer to is that the developer can *and should have* used `Clock`. In our specific case, we can't and I've exposed why on number 1. – Frankie Aug 14 '13 at 18:56
  • I am thinking if it could be solved with using a custom classloader which would load a custom `java.lang.System` – Katona Aug 14 '13 at 19:10
  • @Katona we've tried it but to the same avail as the mock tool, worked only inside the class that called the ClassLoader (should have stated that in the question, sorry). – Frankie Aug 14 '13 at 19:11
  • Check out jMock with CGLIB: http://jmock.org/jmock1-cglib.html The example there mocks java.awt.Graphics. – Jason C Aug 14 '13 at 19:16
  • 1
    well, replacing the system classloader using `-Djava.system.class.loader` option? – Katona Aug 14 '13 at 19:20
  • 1
    Also as for the ClassLoader approach, you can specify a custom system class loader on the JVM command line; that *should* affect all libraries loaded by that JVM: `java -Djava.system.class.loader=your.package.CustomClassLoader ...` – Jason C Aug 14 '13 at 19:20
  • I will try all the options given in the comments and let you know of the results. It'll obviously take a couple of days. If anyone here knows for sure that `x` or `y` will work please state it explicitly. – Frankie Aug 14 '13 at 19:23
  • What about using `System.nanoTime()`? – Michael Aug 14 '13 at 19:28
  • @Michael `System.nanoTime()` returns a "nanosecond-precise time, relative to some arbitrary point." It's good to measure time but cannot manipulate time the way we need to. – Frankie Aug 14 '13 at 19:31
  • It appears that you have successfully replaced System.currentTimeMillis() and youralternative implementation is causing the JVM to crash somewhere. To test, you should replace the it with an identical implementation to see if the crash disappears. This is posted for somebody in chat that has less than 50 rep.\ – nanofarad Aug 14 '13 at 19:48
  • @hexafraction good idea. Will try it and get back to you tomorrow. – Frankie Aug 14 '13 at 19:59
  • @Frankie If you could pop into chat, [this user](http://chat.stackoverflow.com/users/1981415/daveloyall) suggested it. – nanofarad Aug 14 '13 at 20:00
  • If you declare a `new MockUp(){…}` in your main, I would have thought all your code would inherit the mocked class. – assylias Aug 14 '13 at 20:41
  • @hexafraction thanks for your comments. As you can see I can comment now. :) I followed your suggestion to post the idea as an answer. Cheers! – daveloyall Aug 14 '13 at 21:05

4 Answers4

10

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:

public aspect CurrentTimeInMillisMethodCallChanger {

    long around(): 
       call(public static native long java.lang.System.currentTimeMillis()) 
       && within(user.code.base.pckg.*) {
         return 0; //provide your own implementation returning a long
    }
}
Nándor Előd Fekete
  • 6,988
  • 1
  • 22
  • 47
  • While this looks very promising it still fails on the scenario where the user captures date from an external library. Say Joda was used. `new DateTime()` would give back the correct system time, right? I'm asking because I've never used AspectJ before and while I was able to run and compile the code you provided (thanks, by the way) I was unable to have Joda report milis as 0. Joda is not compiled/weaved by AspectJ and, therefore, feeds back the system time. Am I right in assuming this? – Frankie Aug 20 '13 at 16:57
  • 1
    You can also weave the Joda library. The org.joda.time.DateTimeUtils.SystemMillisProvider is using System.currentTimeMillis(), so if it's replaced (either by classpath order, classloading, or weaving), it will do whatever you want. There are also other sublcasses of org.joda.time.DateTimeUtils.MillisProvider in Joda, so I suppose it can be configured to use another provider an then you don't even have to change the SystemMillisProvider. If other libraries are involved, they can also be changed by weaving, though you'll end up with multiple special weaved version of those libraries. – Nándor Előd Fekete Aug 20 '13 at 17:26
  • Every single library out there, even `Date()`, fall back to `System.currentTimeMilis()` so if we can change it they should all work as expected. To run your example I read the docs and compiled the code using `ajc`. From this reply I inferred that you can "weave" at runtime. I will be reading the docs to try and do it, if you can expand the answer or give me some points on the right track I'd be much appreciated. Thanks! – Frankie Aug 20 '13 at 17:52
  • You can use load time weaving, but it might be a bit more difficult to set up. Though I never tried it, I would advise you strongly against trying to weave JDK classes at load time, because the LTW agent itself is loaded by the JDK at bootstrap, so you would end up with some JDK classes weaved and some not. I see a lot more potential problems with that route. The route I would choose is to weave at compile/build time, and weave all points of code at the border between user code and JDK code, so that all invocations that end up calling System.currentTimeMillis() get replaced by your custom code – Nándor Előd Fekete Aug 20 '13 at 20:31
  • The only drawback I see with compile/build time weaving is that you end up with new versions of every class from all weaved libraries, so you'll have to take care of these if you have to package your application. If it's only for unit testing, it's much easier, because you don't have to package the weaved classes, just build the classpath and run the tests. – Nándor Előd Fekete Aug 20 '13 at 20:35
  • 1
    I've tried both but went with LTW (load-time-weaving) as it provides exceptional adaptability to all sorts of possible user-loaded classes (including those loaded with custom class-loaders). Re-defined `currentTimeMillis`, `Date` and `Calendar`. Joda and all similar work instantly out-of-the-box as LTW weaves their calls. Was also able to capture `wait` and `sleep` that will have to be re-defined to make things fully functional (as Timers rely on wait) but, so far, this route has produced wonderful results. Thank you very much for the help and for providing relevant and extremely helpful code. – Frankie Aug 21 '13 at 18:32
  • @Frankie, could you please indicate how you replaced portions of types from rt.jar via LTW? Since rt.jar is in the boot classpath, AspectJ's load-time weaver appears not to get a chance to do its work. Thanks. – Christian Conti-Vock Jun 16 '17 at 20:35
  • A link to a complete working example would be nice. – PeMa Jul 15 '20 at 14:04
  • @Frankie I'm unable to weave aspect pointcut using this below. Can u pls indicate what is wrong? @Pointcut("call(public static native long java.lang.System.currentTimeMillis()) && !cflow(adviceexecution()") – Ankur Nov 17 '20 at 20:22
  • HI @agargi. It's a bit hard just from that line. Can you put a fully working example on github? I'll give it a look! – Frankie Nov 17 '20 at 23:54
0

I'm not 100% sure if I oversee something here, but you can create your own System class like this:

public static class System {
    static PrintStream err = System.err;
    static InputStream in = System.in;
    static PrintStream out = System.out;

    static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) {
        System.arraycopy(src, srcPos, dest, destPos, length);
    }

    // ... and so on with all methods (currently 26) except `currentTimeMillis()`

    static long currentTimeMillis() {
        return 4711L; // Your application specific clock value
    }
}

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 specific System 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.

jboi
  • 11,324
  • 4
  • 36
  • 43
  • 2
    Thanks for the input but this fails on number #1. We can't replace the code because it's uploaded by an endless number of different developers. And, if we could, we should use an abstraction of date, like `Clock` and have it return a true or fake time depending on our needs, that would be less *messy* than re-defining System. I'm still slowly looking into the other feedback and will report any findings. – Frankie Aug 15 '13 at 11:29
0

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:

"How do you provide an altered System.currentTimeMillis() to application classes, but leave the default implementation in place for core classes?"

daveloyall
  • 2,140
  • 21
  • 23
  • You were right in assuming I had been able to replace `System` but had an error. If I copy Sun's `System.java` code I can mess with it, create my own methods, change implemented methods but, unfortunately, `currentTimeMillis()` is declared as a `public static native long` and I can't mess with the declaration as the VM crashes. I've dug into it a bit but eventually I'd have to change native proprietary code which, while may be feasible, makes it a long shot. Will try other approaches and keep reporting. Will also edit question accordingly. Thanks. – Frankie Aug 15 '13 at 15:25
0

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).

Arthur
  • 11
  • 1
  • As stated on #1 there is no way we can change the user-code. And while I can relate to your logic - decompile the whole thing - change all calls to `currentTimeMilis()`- compile again; it looks way to messy. ASM and BCEL may be better alternatives though I'm still investigating. Many thanks for the input. – Frankie Aug 19 '13 at 16:46
  • 1
    FWIW i managed to do this with Javassist in a Java agent `premain` using `instrumentation.retransformClasses(java.lang.System.class);` with much less trouble than i had expected. – Christoffer Hammarström Mar 27 '19 at 15:26