views:

878

answers:

6

I have a Java program that loads thirdparty class files (classes I did not write) and executes them. These classes often use java.util.Random, which by default generates random starting seed values every time it gets instantiated. For reasons of reproducability, I want to give these classes the same starting seed every time, changing it only at my discretion.

Here are some of the obvious solutions, and why they don't work:

  1. Use a different Random class in the thirdparty classfiles. The problem here is I only load the class files, and cannot modify the source.

  2. Use a custom classloader to load our own Random class instead of the JVM's version. This approach will not work because Java does not allow classloaders to override classes in the java package.

  3. Swap out the rt.jar's java.util.Random implementation for our own, or putting files into trusted locations for the JVM. These approaches require the user of the application messing with the JVM install on their machine, and are no good.

  4. Adding a custom java.util.Random class to the bootclasspath. While this would technically work, for this particular application, it is impractical because this application is intended for end users to run from an IDE. I want to make running the app convenient for users, which means forcing them to set their bootclasspath is a pain. I can't hide this in a script, because it's intended to be run from an IDE like Eclipse (for easy debugging.)

So how can I do this?

+1  A: 

You could use AOP to intercept the calls to Random and twiddle the arg to what you want.

Sam

Sam Reynolds
A: 

"Use a custom classloader to load our own Random class instead of the JVM's version. This approach will not work because Java does not allow classloaders to override classes in the java package."

how about changing the bootclasspath to use your custom Random class ?

BR, ~A

anjanb
Changing the bootclasspath is effective, but requires modifying the way the Java program is run. This is fine if you can control the way someone launches the program, e.g. a startup script, but in my case users typically run from within an IDE and would have to do it manually, which is not good.
adum
+1  A: 

Although you may not change the classloader trivially for "java.x" and "sun.x" packages, there is a way to reckon class loading (and install a "after class was bytecoded and loaded" listener) of theses classes, so you could set something like the seed after loading the classes from these packages. Hint: Use reflection.

Anyway, as long as I don't have further informations what exactly you want to achieve, it's pretty hard to help you here.

P.S.: Be aware that "static {}" - blocks may hinder you messing around with seeds, again.

Georgi
+3  A: 

Your option 2 will actually work, with the following directions.

You will need ( as anjab said ) to change the bootstrap class path .

In the command line of the program you need to add the following:

java -Xbootclasspath/p:C:\your\random_impl.jar YourProgram

Assuming you're on Windown machine or the path for that matter in any OS.

That option adds the classes in jar files before the rt.jar are loaded. So your Random will be loaded before the rt.jar Random class does.

The usage is displayed by typing :

java -X

It displays all the X(tra) features de JVM has. It may by not available on other VM implementations such as JRockit or other but it is there on Sun JVM.

-Xbootclasspath/p: prepend in front of bootstrap class path

I've use this approach in an application where the default ORB class should be replaced with others ORB implementation. ORB class is part of the Java Core and never had any problem.

Good luck.

OscarRyz
+1  A: 

Consider modifying the third party libraries to have them use a seen for their Random instances. Though you do not have the source code, you can probably edit the bytecode to do it. One helpful toolkit for doing such is ASM.

Dave L.
A: 

Yes option 2 is working: created two classes for testing propse named ThirdPartyClass.java and Random.java

created jar from ThirdPartyClass.class jar -cvf tpc.jar ThirdPartyClass.class

created jar from Random.class jar -cvf rt123.jar Random.class

after that execute with following command:

java -Xbootclasspath/p:tcp.jar:rt123.jar -cp . -verbose ThirdPartyClass o/p will be -- seed value for ThirdPartyClass-> 1

source code ThirdPartyClass.java----->

import java.util.Random; public class ThirdPartyClass { ThirdPartyClass(long seed ) { System.out.println("seed value for ThirdPartyClass-> "+seed); }
public static void main(String [] args) { ThirdPartyClass tpc=new ThirdPartyClass(new Random().nextLong()); } }

source code Random.java------->

package java.util; import java.io.Serializable; public class Random extends Object implements Serializable { public Random() { } public Random(long seed) { } public long nextLong() { return 1; } }

Thanks Mahaveer Prasad Mali

mahaveer