mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-02 11:05:29 -04:00
Better method naming.
Code cleanup. Doc cleanup.
This commit is contained in:
parent
07a5c62a28
commit
f7bc246bad
@ -38,21 +38,27 @@ import java.security.SecureRandom;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A factory for creating UUIDs not directly supported by {@link java.util.UUID}.
|
||||
* A factory for creating {@code UUID}s not directly supported by {@link java.util.UUID java.util.UUID}.
|
||||
* <p>
|
||||
* This class can create
|
||||
* version 1 time based, using either IEEE 802 (mac) address or random "node" value
|
||||
* and version 5 SHA1 hash based UUIDs.
|
||||
* This class can create version 1 time based {@code UUID}s, using either IEEE 802 (mac) address or random "node" value
|
||||
* as well as version 5 SHA1 hash based {@code UUID}s.
|
||||
* </p>
|
||||
* <p>
|
||||
* The node value for version 1 UUIDs will, by default, reflect the IEEE 802 (mac) address of one of
|
||||
* The timestamp value for version 1 {@code UUID}s will use a high precision clock, when available to the Java VM.
|
||||
* If the Java system clock does not offer the needed precision, the timestamps will fall back to 100-nanosecond
|
||||
* increments, to avoid duplicates.
|
||||
* </p>
|
||||
* <p>
|
||||
* <a name="mac-node"></a>
|
||||
* The node value for version 1 {@code UUID}s will, by default, reflect the IEEE 802 (mac) address of one of
|
||||
* the network interfaces of the local computer.
|
||||
* This node value can be manually overridden by setting
|
||||
* the system property {@code "com.twelvemonkeys.util.UUID.node"} to a valid IEEE 802 address, on the form
|
||||
* {@code 12:34:56:78:9a:bc} or {@code 12-34-45-78-9a-bc}.
|
||||
* </p>
|
||||
* <p>
|
||||
* The node value for the random "node" based version 1 UUIDs will be stable for the lifetime of the VM.
|
||||
* <a name="random-node"></a>
|
||||
* The node value for the random "node" based version 1 {@code UUID}s will be stable for the lifetime of the VM.
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
@ -67,12 +73,12 @@ public final class UUIDFactory {
|
||||
private static final String NODE_PROPERTY = "com.twelvemonkeys.util.UUID.node";
|
||||
|
||||
/**
|
||||
* Nil UUID: {@code "00000000-0000-0000-0000-000000000000"}.
|
||||
* The Nil UUID: {@code "00000000-0000-0000-0000-000000000000"}.
|
||||
*
|
||||
* The nil UUID is special form of UUID that is specified to have all
|
||||
* 128 bits set to zero. Not particularly useful.
|
||||
* 128 bits set to zero. Not particularly useful, unless as a placeholder or template.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc4122#section-4.1.7">RFC 4122</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc4122#section-4.1.7">RFC 4122 4.1.7. Nil UUID</a>
|
||||
*/
|
||||
public static final UUID NIL = new UUID(0l, 0l);
|
||||
|
||||
@ -86,34 +92,7 @@ public final class UUIDFactory {
|
||||
static final long SECURE_RANDOM_NODE = getSecureRandomNode();
|
||||
|
||||
private static long getSecureRandomNode() {
|
||||
/*
|
||||
Obtain a 47-bit cryptographic quality random
|
||||
number and use it as the low 47 bits of the node ID, with the least
|
||||
significant bit of the first octet of the node ID set to one. This
|
||||
bit is the unicast/multicast bit, which will never be set in IEEE 802
|
||||
addresses obtained from network cards. Hence, there can never be a
|
||||
conflict between UUIDs generated by machines with and without network
|
||||
cards. (Recall that the IEEE 802 spec talks about transmission
|
||||
order)
|
||||
*/
|
||||
|
||||
/*
|
||||
In addition, items such as the computer's name and the name of the
|
||||
operating system, while not strictly speaking random, will help
|
||||
differentiate the results from those obtained by other systems.
|
||||
|
||||
The exact algorithm to generate a node ID using these data is system
|
||||
specific, because both the data available and the functions to obtain
|
||||
them are often very system specific. A generic approach, however, is
|
||||
to accumulate as many sources as possible into a buffer, use a
|
||||
message digest such as MD5 [4] or SHA-1 [8], take an arbitrary 6
|
||||
bytes from the hash value, and set the multicast bit as described
|
||||
above.
|
||||
*/
|
||||
|
||||
// TODO: Verify that nextLong is still cryptographically strong after the bit masking
|
||||
// TODO: Consider using the hashing approach above
|
||||
|
||||
// Creates a completely random "node" value, with the unicast bit set to 1, as outlined in RFC 4122.
|
||||
return 1l << 40 | SECURE_RANDOM.nextLong() & 0xffffffffffffl;
|
||||
}
|
||||
|
||||
@ -171,11 +150,20 @@ public final class UUIDFactory {
|
||||
|
||||
private UUIDFactory() {}
|
||||
|
||||
// See also http://tools.ietf.org/html/rfc4122#appendix-B
|
||||
// See http://tools.ietf.org/html/rfc4122: 4.3. Algorithm for Creating a Name-Based UUID
|
||||
// TODO: Naming (of the method)
|
||||
// TODO: Read up on creating these UUIDs in RFC, mentions something about UUID for namespace as input..?
|
||||
static UUID nameUUIDFromBytesSHA1(byte[] name) {
|
||||
/**
|
||||
* Creates a type 5 (name based) {@code UUID} based on the specified byte array.
|
||||
* This method is effectively identical to {@link UUID#nameUUIDFromBytes}, except that this method
|
||||
* uses a SHA1 hash instead of the MD5 hash used in the type 3 {@code UUID}s.
|
||||
* RFC 4122 states that "SHA-1 is preferred" over MD5, without giving a reason for why.
|
||||
*
|
||||
* @param name a byte array to be used to construct a {@code UUID}
|
||||
* @return a {@code UUID} generated from the specified array.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc4122#section-4.3">RFC 4122 4.3. Algorithm for Creating a Name-Based UUID</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc4122#appendix-A">RFC 4122 appendix A</a>
|
||||
* @see UUID#nameUUIDFromBytes(byte[])
|
||||
*/
|
||||
public static UUID nameUUIDv5FromBytes(byte[] name) {
|
||||
// Based on code from OpenJDK UUID#nameUUIDFromBytes + private byte[] constructor
|
||||
MessageDigest md;
|
||||
|
||||
@ -207,26 +195,39 @@ public final class UUIDFactory {
|
||||
return new UUID(msb, lsb);
|
||||
}
|
||||
|
||||
// Creatse version 1 node based UUIDs as specified in rfc422
|
||||
// See http://tools.ietf.org/html/rfc4122#appendix-B
|
||||
// See http://en.wikipedia.org/wiki/MAC_address
|
||||
// TODO: Naming (of the method)
|
||||
static UUID timeNodeBasedV1() {
|
||||
/**
|
||||
* Creates a version 1 time (and node) based {@code UUID}.
|
||||
* The node part is by default the IEE 802 (mac) address of one of the network cards of the current machine.
|
||||
*
|
||||
* @return a {@code UUID} based on the current time and the node address of this computer.
|
||||
* @see <a href="http://tools.ietf.org/html/rfc4122#section-4.2">RFC 4122 4.2. Algorithms for Creating a Time-Based UUID</a>
|
||||
* @see <a href="http://en.wikipedia.org/wiki/MAC_address">IEEE 802 (mac) address</a>
|
||||
* @see <a href="#mac-node">Overriding the node address</a>
|
||||
*
|
||||
* @throws IllegalStateException if the IEEE 802 (mac) address of the computer (node) cannot be found.
|
||||
*/
|
||||
public static UUID timeNodeBasedUUID() {
|
||||
if (MAC_ADDRESS_NODE == -1) {
|
||||
// TODO: OR fall back to Random??
|
||||
throw new IllegalStateException("Could not determine IEEE 802 (mac) address for node");
|
||||
}
|
||||
|
||||
return new UUID(createTimeAndVersion(), createClockSeqAndNode(MAC_ADDRESS_NODE));
|
||||
}
|
||||
|
||||
// Creates version 1 "node" based UUIDs, using 47 bit secure random number + lsb of first octet
|
||||
// (unicast/multicast bit) set to 1 as described in rfc422: 4.5. Node IDs that Do Not Identify the Host
|
||||
// See http://tools.ietf.org/html/rfc4122#appendix-B
|
||||
// TODO: Naming (of the method)
|
||||
// TODO: Document that these can never clash with node based v1 UUIDs due to unicast/multicast bit
|
||||
// However, no uniqueness between multiple mavhines/vms/nodes can be guaranteed.
|
||||
static UUID timeRandomBasedV1() {
|
||||
/**
|
||||
* Creates a version 1 time based {@code UUID} with the node part replaced by a random based value.
|
||||
* The node part is computed using a 47 bit secure random number + lsb of first octet (unicast/multicast bit) set to 1.
|
||||
* These {@code UUID}s can never clash with "real" node based version 1 {@code UUID}s due to the difference in
|
||||
* the unicast/multicast bit, however, no uniqueness between multiple machines/vms/nodes can be guaranteed.
|
||||
*
|
||||
* @return a {@code UUID} based on the current time and a random generated "node" value.
|
||||
* @see <a href="http://tools.ietf.org/html/rfc4122#section-4.5">RFC 4122 4.5. Node IDs that Do Not Identify the Host</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc4122#appendix-A">RFC 4122 Appendix A</a>
|
||||
* @see <a href="#random-node">Lifetime of random node value</a>
|
||||
*
|
||||
* @throws IllegalStateException if the IEEE 802 (mac) address of the computer (node) cannot be found.
|
||||
*/
|
||||
public static UUID timeRandomBasedUUID() {
|
||||
return new UUID(createTimeAndVersion(), createClockSeqAndNode(SECURE_RANDOM_NODE));
|
||||
}
|
||||
|
||||
@ -261,7 +262,7 @@ public final class UUIDFactory {
|
||||
* @return a comparator that compares UUIDs as 128 bit unsigned entities.
|
||||
*
|
||||
* @see <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7025832">java.lang.UUID compareTo() does not do an unsigned compare</a>
|
||||
* @see
|
||||
* @see <a href="http://tools.ietf.org/html/rfc4122#appendix-A">RFC 4122 Appendix A</a>
|
||||
*/
|
||||
public static Comparator<UUID> comparator() {
|
||||
return COMPARATOR;
|
||||
@ -299,14 +300,13 @@ public final class UUIDFactory {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A high-resolution timer for use in creating version 1 UUIDs.
|
||||
* A high-resolution timer for use in creating version 1 {@code UUID}s.
|
||||
*/
|
||||
static final class Clock {
|
||||
// Java: 0:00, Jan. 1st, 1970 vs UUID: 0:00, Oct 15th, 1582
|
||||
private static final long JAVA_EPOCH_OFFSET = 122192928000000000L;
|
||||
private static final long JAVA_EPOCH_OFFSET_HUNDRED_NANOS = 122192928000000000L;
|
||||
|
||||
private static int clockSeq = SECURE_RANDOM.nextInt();
|
||||
|
||||
@ -324,7 +324,7 @@ public final class UUIDFactory {
|
||||
long millis = System.currentTimeMillis();
|
||||
long nanos = System.nanoTime();
|
||||
|
||||
initialTime = JAVA_EPOCH_OFFSET + millis * 10000 + (nanos / 100) % 10000;
|
||||
initialTime = JAVA_EPOCH_OFFSET_HUNDRED_NANOS + millis * 10000 + (nanos / 100) % 10000;
|
||||
initialNanos = nanos;
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ public class UUIDFactoryTest {
|
||||
@Test
|
||||
public void testVersion3NameBasedMD5NotEqualSHA1() throws UnsupportedEncodingException {
|
||||
UUID a = UUID.nameUUIDFromBytes(EXAMPLE_COM_UUID.getBytes("UTF-8"));
|
||||
assertFalse(a.equals(UUIDFactory.nameUUIDFromBytesSHA1(EXAMPLE_COM_UUID.getBytes("UTF-8"))));
|
||||
assertFalse(a.equals(UUIDFactory.nameUUIDv5FromBytes(EXAMPLE_COM_UUID.getBytes("UTF-8"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -64,27 +64,27 @@ public class UUIDFactoryTest {
|
||||
|
||||
@Test
|
||||
public void testVersion5NameBasedSHA1VariantVersion() throws UnsupportedEncodingException {
|
||||
UUID a = UUIDFactory.nameUUIDFromBytesSHA1(EXAMPLE_COM_UUID.getBytes("UTF-8"));
|
||||
UUID a = UUIDFactory.nameUUIDv5FromBytes(EXAMPLE_COM_UUID.getBytes("UTF-8"));
|
||||
assertEquals(2, a.variant());
|
||||
assertEquals(5, a.version());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVersion5NameBasedSHA1Equals() throws UnsupportedEncodingException {
|
||||
UUID a = UUIDFactory.nameUUIDFromBytesSHA1(EXAMPLE_COM_UUID.getBytes("UTF-8"));
|
||||
UUID b = UUIDFactory.nameUUIDFromBytesSHA1(EXAMPLE_COM_UUID.getBytes("UTF-8"));
|
||||
UUID a = UUIDFactory.nameUUIDv5FromBytes(EXAMPLE_COM_UUID.getBytes("UTF-8"));
|
||||
UUID b = UUIDFactory.nameUUIDv5FromBytes(EXAMPLE_COM_UUID.getBytes("UTF-8"));
|
||||
assertEquals(a, b);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVersion5NameBasedSHA1NotEqualMD5() throws UnsupportedEncodingException {
|
||||
UUID a = UUIDFactory.nameUUIDFromBytesSHA1(EXAMPLE_COM_UUID.getBytes("UTF-8"));
|
||||
UUID a = UUIDFactory.nameUUIDv5FromBytes(EXAMPLE_COM_UUID.getBytes("UTF-8"));
|
||||
assertFalse(a.equals(UUID.nameUUIDFromBytes(EXAMPLE_COM_UUID.getBytes("UTF-8"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVersion5NameBasedSHA1FromStringRep() throws UnsupportedEncodingException {
|
||||
UUID a = UUIDFactory.nameUUIDFromBytesSHA1(EXAMPLE_COM_UUID.getBytes("UTF-8"));
|
||||
UUID a = UUIDFactory.nameUUIDv5FromBytes(EXAMPLE_COM_UUID.getBytes("UTF-8"));
|
||||
assertEquals(a, UUID.fromString(a.toString()));
|
||||
}
|
||||
|
||||
@ -92,37 +92,36 @@ public class UUIDFactoryTest {
|
||||
|
||||
@Test
|
||||
public void testVersion1NodeBasedVariantVersion() {
|
||||
UUID uuid = UUIDFactory.timeNodeBasedV1();
|
||||
UUID uuid = UUIDFactory.timeNodeBasedUUID();
|
||||
assertEquals(2, uuid.variant());
|
||||
assertEquals(1, uuid.version());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVersion1NodeBasedMacAddress() {
|
||||
UUID uuid = UUIDFactory.timeNodeBasedV1();
|
||||
UUID uuid = UUIDFactory.timeNodeBasedUUID();
|
||||
assertEquals(UUIDFactory.MAC_ADDRESS_NODE, uuid.node());
|
||||
// TODO: Test that this is actually a Mac address from the local computer, or specified through system property?
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVersion1NodeBasedFromStringRep() {
|
||||
UUID uuid = UUIDFactory.timeNodeBasedV1();
|
||||
UUID uuid = UUIDFactory.timeNodeBasedUUID();
|
||||
assertEquals(uuid, UUID.fromString(uuid.toString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVersion1NodeBasedClockSeq() {
|
||||
UUID uuid = UUIDFactory.timeNodeBasedV1();
|
||||
UUID uuid = UUIDFactory.timeNodeBasedUUID();
|
||||
assertEquals(UUIDFactory.Clock.getClockSequence(), uuid.clockSequence());
|
||||
|
||||
// Test time fields (within reasonable limits +/- 100 ms or so?)
|
||||
// TODO: Compare with system clock for sloppier resolution
|
||||
assertEquals(UUIDFactory.Clock.currentTimeHundredNanos(), uuid.timestamp(), 1e6);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVersion1NodeBasedTimestamp() {
|
||||
UUID uuid = UUIDFactory.timeNodeBasedV1();
|
||||
UUID uuid = UUIDFactory.timeNodeBasedUUID();
|
||||
// Test time fields (within reasonable limits +/- 100 ms or so?)
|
||||
assertEquals(UUIDFactory.Clock.currentTimeHundredNanos(), uuid.timestamp(), 1e6);
|
||||
}
|
||||
@ -131,7 +130,7 @@ public class UUIDFactoryTest {
|
||||
public void testVersion1NodeBasedUniMulticastBitUnset() {
|
||||
// Do it a couple of times, to avoid accidentally have correct bit
|
||||
for (int i = 0; i < 100; i++) {
|
||||
UUID uuid = UUIDFactory.timeNodeBasedV1();
|
||||
UUID uuid = UUIDFactory.timeNodeBasedUUID();
|
||||
assertEquals(0, (uuid.node() >> 40) & 1);
|
||||
}
|
||||
}
|
||||
@ -139,15 +138,15 @@ public class UUIDFactoryTest {
|
||||
@Test
|
||||
public void testVersion1NodeBasedUnique() {
|
||||
for (int i = 0; i < 100; i++) {
|
||||
UUID a = UUIDFactory.timeNodeBasedV1();
|
||||
UUID b = UUIDFactory.timeNodeBasedV1();
|
||||
UUID a = UUIDFactory.timeNodeBasedUUID();
|
||||
UUID b = UUIDFactory.timeNodeBasedUUID();
|
||||
assertFalse(a.equals(b));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVersion1SecureRandomVariantVersion() {
|
||||
UUID uuid = UUIDFactory.timeRandomBasedV1();
|
||||
UUID uuid = UUIDFactory.timeRandomBasedUUID();
|
||||
|
||||
assertEquals(2, uuid.variant());
|
||||
assertEquals(1, uuid.version());
|
||||
@ -155,25 +154,25 @@ public class UUIDFactoryTest {
|
||||
|
||||
@Test
|
||||
public void testVersion1SecureRandomNode() {
|
||||
UUID uuid = UUIDFactory.timeRandomBasedV1();
|
||||
UUID uuid = UUIDFactory.timeRandomBasedUUID();
|
||||
assertEquals(UUIDFactory.SECURE_RANDOM_NODE, uuid.node());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVersion1SecureRandomFromStringRep() {
|
||||
UUID uuid = UUIDFactory.timeRandomBasedV1();
|
||||
UUID uuid = UUIDFactory.timeRandomBasedUUID();
|
||||
assertEquals(uuid, UUID.fromString(uuid.toString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVersion1SecureRandomClockSeq() {
|
||||
UUID uuid = UUIDFactory.timeRandomBasedV1();
|
||||
UUID uuid = UUIDFactory.timeRandomBasedUUID();
|
||||
assertEquals(UUIDFactory.Clock.getClockSequence(), uuid.clockSequence());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVersion1SecureRandomTimestamp() {
|
||||
UUID uuid = UUIDFactory.timeRandomBasedV1();
|
||||
UUID uuid = UUIDFactory.timeRandomBasedUUID();
|
||||
|
||||
// Test time fields (within reasonable limits +/- 100 ms or so?)
|
||||
assertEquals(UUIDFactory.Clock.currentTimeHundredNanos(), uuid.timestamp(), 1e6);
|
||||
@ -183,7 +182,7 @@ public class UUIDFactoryTest {
|
||||
public void testVersion1SecureRandomUniMulticastBit() {
|
||||
// Do it a couple of times, to avoid accidentally have correct bit
|
||||
for (int i = 0; i < 100; i++) {
|
||||
UUID uuid = UUIDFactory.timeRandomBasedV1();
|
||||
UUID uuid = UUIDFactory.timeRandomBasedUUID();
|
||||
assertEquals(1, (uuid.node() >> 40) & 1);
|
||||
}
|
||||
}
|
||||
@ -191,8 +190,8 @@ public class UUIDFactoryTest {
|
||||
@Test
|
||||
public void testVersion1SecureRandomUnique() {
|
||||
for (int i = 0; i < 100; i++) {
|
||||
UUID a = UUIDFactory.timeRandomBasedV1();
|
||||
UUID b = UUIDFactory.timeRandomBasedV1();
|
||||
UUID a = UUIDFactory.timeRandomBasedUUID();
|
||||
UUID b = UUIDFactory.timeRandomBasedUUID();
|
||||
assertFalse(a.equals(b));
|
||||
}
|
||||
}
|
||||
@ -217,7 +216,7 @@ public class UUIDFactoryTest {
|
||||
service.shutdown();
|
||||
assertTrue("Execution timed out", service.awaitTermination(10, TimeUnit.SECONDS));
|
||||
|
||||
Arrays.sort(times);
|
||||
Arrays.sort(times); // This is what really takes time...
|
||||
|
||||
for (int i = 0, timesLength = times.length; i < timesLength; i++) {
|
||||
if (i == 0) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user