java.rmi.server.UID not unique

I need to generate unique id's. According to the documentation, java.rmi.server.UID is guaranteed to generate ID's unique to a physical computer. however, I need to run multiple instances of a program on the same physical computer and if they start up within the same millisecond I get the same UID.

Test program:

import java.net.InetAddress;

import java.net.UnknownHostException;

import java.rmi.server.UID;

/*

* UIDTest.java

*

* Attempts to create a unique ID.

*

*/

public class UIDTest {

String makeUniqueId() {

String firstPart = "localhost:127.0.0.1";

try {

InetAddress myInetAddr = InetAddress.getLocalHost();

firstPart = myInetAddr.getHostName() + ":" + myInetAddr.getHostAddress();

} catch (UnknownHostException e) {

}

UID uid = new UID();

return firstPart + ":" + uid;

}

public static void main(String[] args) {

UIDTest myTest = new UIDTest();

String myUniqueId = myTest.makeUniqueId();

System.out.println("Generated Unique ID = " + myUniqueId);

System.out.println("Random number: " + java.lang.Math.random());

System.out.println("Object's IdentityHashCode: " + System.identityHashCode(myTest));

}

}

Batch file for Microsoft Windows:

start cmd /k java UIDTest

start cmd /k java UIDTest

Both instances produce the same output:

Generated Unique ID = MyCPU:111.22.33.44:b8df17:10cef9e1c0d:-8000

Random number: 0.8841158985227917

Object's IdentityHashCode: 27837671

When you run this you may find that sometimes both instances generate the same result and sometimes not. I need a guaranteed unique result.

[1753 byte] By [mluttona] at [2007-9-25]
# 1
java.util.UUID @since 1.5
ejpa at 2007-7-14 > top of java,Core,Core APIs...
# 2
Until management allows us to qualify and upgrade all the servers, I'm stuck with 1.4.Does 5.0 implement UID according to spec?
mluttona at 2007-7-14 > top of java,Core,Core APIs...
# 3

I believe so. http://java.sun.com/j2se/1.5.0/docs/api/java/util/UUID.html.

OTOH I don't understand why you're getting the original problem. java.rmi.server.UID is supposed to be unique w.r.t. time, and it uses a SecureRandom (for the VMID) which should practically never repeat, and a sleep to ensure that it is unique by at least a granularity of one second, not just a millisecond.

ejpa at 2007-7-14 > top of java,Core,Core APIs...
# 4

The problem never happened when we ran the two instances on two physical computers, because I add the computer's IP address to the UID. It would not happen if we ran two threads within the same JVM because UID is unique within a JVM.

But we run two JVM's on the same physical box. One JVM does not affect the other. They both start at the same time, hence random() on each returns the same result. They both start at the same virtual address in their respective process spaces.

java.rmi.server.UID is guaranteed by definition to be unique within a physical computer. From the docs:

"A UID instance contains three primitive values:

unique, an int that uniquely identifies the VM that this UID was generated in, with respect to its host and at the time represented by the time value (an example implementation of the unique value would be a process identifier) ...."

This version (Sun Java HotSpot 1.4.2_08) breaks this guarantee and 1.5 appears to do the same. It does not use the process identifier.

Our workaround is to tell the operations staff to start one process, wait a few seconds and then start the other. Sometimes they do this; sometimes they don't.

I am looking for a pure Java workaround that can distinguish between two separate JVM's. The only thing I can think of at this time is to use JNI to get the underlying process ID. I don't want to make it non-portable.

Also I don't see any guarantee in the java.util.UUID docs that UUID is unique for two separate processes in the same physical computer -- the guarantee that jama.rmi.server.UID makes and HotSpot breaks. The links to "UUIDs and GUIDs" and "ISO/IEC 11578:1996" in the cited API doc are both broken so I can't see whether they address this issue.

mluttona at 2007-7-14 > top of java,Core,Core APIs...
# 5

OK, maybe I should restate this as a puzzle.

Write any Java 1.4 program (not using JNI) such that:

1. when run in two separate processes on the same physical computer, both starting up at the same instant (within a few microseconds), is guaranteed by the language to produce different outputs, and

2. when thus done using Sun's HotSpot JVM 1.4, actually does produce different outputs.

My program fails #2 because of a bug in the JVM.

mluttona at 2007-7-14 > top of java,Core,Core APIs...
# 6

A bit of a hack, I know, but should be portable.

Claim a range of ports (just two ports as you describe it) for your app. When the app starts up, it attempts to open a ServerSocket on the lowest numbered port in the range. It walks up the port numbers until it finds one that's not already open, opens it, and keeps it open for its entire life.

It can use the port number as a salt, or use the offset from the base port number as a time to sleep before generating the UID.

Or just pass different command line args to distinguish the two apps.

Or run a third process whose job is simply to act as a UID broker, and have these guys ask it for a UID.

Or play with java.io.File.createTempFile, though you may run into the same problem. You'd have to investigate the details yourself.

jverda at 2007-7-14 > top of java,Core,Core APIs...
# 7

Thanks! That solution WILL work in our application because:

1. Ports are a resource on the physical machine that cannot be shared across virtual machines.

2. Java can get a port without using JNI.

3. The server runs only one application (two or more instances) so we don't have to worry about interfering with some other application.

4. The application uses TCP/IP so presence of a TCP/IP stack is guaranteed.

Here is the complete solution:

// Uniquer.java

/**

* Class that finds a port exclusive to this virtual machine within the

* physical machine. Singleton.

*

*/

public class Uniquer {

public static final int startPort = 8123;

public static final int endPort = 8133; // Last plus one

// Singleton.

private static Uniquer uniq = new Uniquer();

private final ServerSocket ssock;

private final int port;

// Private constructor

private Uniquer() {

//System.out.println("Creating Uniquer");

ServerSocket s2 = null;

int pt = -1;// If fails to find one

try {

for (pt = startPort; pt < endPort; pt++) {

try {

s2 = new ServerSocket(pt);

//System.out.println(pt);

break; // Exit loop

} catch (Exception e) {

// Throws java.net.BindException

//System.out.println(pt + ":" + e.toString());

// Loop back

}

}

} finally {

ssock = s2;

port = pt;

}

}

// Get port

public int getPort() {

return this.port;

}

// Get instance

public static Uniquer instance() {

return uniq;

}

}

// UIDTest.java

/**

*

* main class

*

*/

public class UIDTest {

// Singleton Uniquer created at startup

protected static Uniquer uniq;

static {

uniq = Uniquer.instance();

}

String makeUniqueId() {

String firstPart = "localhost:127.0.0.1";

try {

InetAddress myInetAddr = InetAddress.getLocalHost();

firstPart = myInetAddr.getHostName() + ":" + myInetAddr.getHostAddress();

} catch (UnknownHostException e) {

}

UID uid = new UID();

int uniquePort = uniq.getPort();

return firstPart + ":" + uid + ":" + uniquePort;

}

public static void main(String[] args) {

//System.out.println("Starting up");

UIDTest myTest = new UIDTest();

String myUniqueId = myTest.makeUniqueId();

System.out.println("Generated Unique ID = " + myUniqueId);

System.out.println("Random number: " + java.lang.Math.random());

System.out.println("Object's IdentityHashCode: " + System.identityHashCode(myTest));

System.out.println("Unique port: " + uniq.getPort());

}

}

Here is the output when run twice at the same time:

Instance 1:

Generated Unique ID = MyCPU:111.22.33.44:1729854:10cfe14298a:-8000:8123

Random number: 0.8659601035554275

Object's IdentityHashCode: 33482492

Unique port: 8123

Instance 2:

Generated Unique ID = MyCPU:111.22.33.44:1729854:10cfe14298a:-8000:8124

Random number: 0.8659601035554275

Object's IdentityHashCode: 33482492

Unique port: 8124

Thanks again.

mluttona at 2007-7-14 > top of java,Core,Core APIs...
# 8
> Thanks! That solution WILL work in our application> because:Cool. I'm glad I was able to help.
jverda at 2007-7-14 > top of java,Core,Core APIs...