Author: eevans
Date: Sat Jan 29 17:25:52 2011
New Revision: 1065079
URL: http://svn.apache.org/viewvc?rev=1065079&view=rev
Log:
CASSANDRA-2067 o.a.c.utils.UUIDGen adapted from flewton
Patch by eevans for CASSANDRA-2067
Modified:
cassandra/trunk/src/java/org/apache/cassandra/utils/UUIDGen.java
Modified: cassandra/trunk/src/java/org/apache/cassandra/utils/UUIDGen.java
URL:
http://svn.apache.org/viewvc/cassandra/trunk/src/java/org/apache/cassandra/utils/UUIDGen.java?rev=1065079&r1=1065078&r2=1065079&view=diff
==============================================================================
--- cassandra/trunk/src/java/org/apache/cassandra/utils/UUIDGen.java (original)
+++ cassandra/trunk/src/java/org/apache/cassandra/utils/UUIDGen.java Sat Jan 29
17:25:52 2011
@@ -1,56 +1,44 @@
package org.apache.cassandra.utils;
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-
import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.nio.ByteBuffer;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
import java.util.UUID;
-import org.safehaus.uuid.EthernetAddress;
-import org.safehaus.uuid.UUIDGenerator;
-
/**
- * Generates type 1 (time-based) UUIDs
+ * The goods are here: www.ietf.org/rfc/rfc4122.txt.
*/
public class UUIDGen
{
- /** creates a type1 uuid but substitutes hash of the IP where the mac
would go. */
- public static synchronized UUID makeType1UUIDFromHost(InetAddress addr)
+ // A grand day! millis at 00:00:00.000 15 Oct 1582.
+ private static final long START_EPOCH = -12219292800000L;
+ private static final long clock = new
Random(System.currentTimeMillis()).nextLong();
+
+ // placement of this singleton is important. It needs to be instantiated
*AFTER* the other statics.
+ private static final UUIDGen instance = new UUIDGen();
+
+ private long lastNanos;
+ private final Map<InetAddress, Long> nodeCache = new HashMap<InetAddress,
Long>();
+
+ private UUIDGen()
{
- try
- {
- MessageDigest digest = MessageDigest.getInstance("MD5");
- digest.update(addr.getAddress());
- byte[] md5 = digest.digest();
- byte[] fauxMac = new byte[6];
- System.arraycopy(md5, 0, fauxMac, 0, Math.min(md5.length,
fauxMac.length));
- return
getUUID(ByteBuffer.wrap(UUIDGenerator.getInstance().generateTimeBasedUUID(new
EthernetAddress(fauxMac)).toByteArray()));
- }
- catch (NoSuchAlgorithmException ex)
- {
- throw new RuntimeException("Your platform has no support for
generating MD5 sums");
- }
+ // make sure someone didn't whack the clock by changing the order of
instantiation.
+ if (clock == 0) throw new RuntimeException("singleton instantiation is
misplaced.");
+ }
+
+ /**
+ * Creates a type 1 UUID (time-based UUID) that substitutes a hash of
+ * an IP address in place of the MAC (unavailable to Java).
+ *
+ * @param addr the host address to use
+ * @return a UUID instance
+ */
+ public static UUID makeType1UUIDFromHost(InetAddress addr)
+ {
+ return new UUID(instance.createTime(System.currentTimeMillis()),
instance.getClockSeqAndNode(addr));
}
/** creates a type 1 uuid from raw bytes. */
@@ -72,4 +60,106 @@ public class UUIDGen
}
return b;
}
+
+ /**
+ * Returns a 16 byte representation of a type 1 UUID (a time-based UUID),
+ * based on the current system time.
+ *
+ * @return a type 1 UUID represented as a byte[]
+ */
+ public static byte[] getTimeUUIDBytes()
+ {
+ return getTimeUUIDBytes(System.currentTimeMillis());
+ }
+
+ /**
+ * Converts a milliseconds-since-epoch timestamp into the 16 byte
representation
+ * of a type 1 UUID (a time-based UUID).
+ *
+ * @param timeMillis
+ * @return a type 1 UUID represented as a byte[]
+ */
+ public static byte[] getTimeUUIDBytes(long timeMillis)
+ {
+ long msb = instance.createTime(timeMillis), lsb =
instance.getClockSeqAndNode();
+ byte[] uuidBytes = new byte[16];
+
+ for (int i = 0; i < 8; i++)
+ uuidBytes[i] = (byte) (msb >>> 8 * (7 - i));
+
+ for (int i = 8; i < 16; i++)
+ uuidBytes[i] = (byte) (lsb >>> 8 * (7 - i));
+
+ return uuidBytes;
+ }
+
+ private long getClockSeqAndNode()
+ {
+ try
+ {
+ return getClockSeqAndNode(InetAddress.getLocalHost());
+ }
+ catch (UnknownHostException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ // todo: could cache value if we assume node doesn't change.
+ private long getClockSeqAndNode(InetAddress addr)
+ {
+ long lsb = 0;
+ lsb |= (clock & 0x3f00000000000000L) >>> 56; // was 58?
+ lsb |= 0x0000000000000080;
+ lsb |= (clock & 0x00ff000000000000L) >>> 48;
+ lsb |= makeNode(addr);
+ return lsb;
+ }
+
+ // needs to return two different values for the same when.
+ // we can generate at most 10k UUIDs per ms.
+ private synchronized long createTime(long when)
+ {
+ long nanosSince = (when - START_EPOCH) * 10000;
+ if (nanosSince > lastNanos)
+ lastNanos = nanosSince;
+ else
+ nanosSince = ++lastNanos;
+
+ long msb = 0L;
+ msb |= (0x00000000ffffffffL & nanosSince) << 32;
+ msb |= (0x0000ffff00000000L & nanosSince) >>> 16;
+ msb |= (0xffff000000000000L & nanosSince) >>> 48;
+ msb |= 0x0000000000001000L; // sets the version to 1.
+ return msb;
+ }
+
+ // Lazily create node hashes, and cache them for later
+ private long makeNode(InetAddress addr)
+ {
+ if (nodeCache.containsKey(addr))
+ return nodeCache.get(addr);
+
+ // ideally, we'd use the MAC address, but java doesn't expose that.
+ byte[] hash =
FBUtilities.hash(ByteBuffer.wrap(addr.toString().getBytes()));
+ long node = 0;
+ for (int i = 0; i < Math.min(6, hash.length); i++)
+ node |= (0x00000000000000ff & (long)hash[i]) << (5-i)*8;
+ assert (0xff00000000000000L & node) == 0;
+
+ nodeCache.put(addr, node);
+
+ return node;
+ }
}
+
+// for the curious, here is how I generated START_EPOCH
+// Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT-0"));
+// c.set(Calendar.YEAR, 1582);
+// c.set(Calendar.MONTH, Calendar.OCTOBER);
+// c.set(Calendar.DAY_OF_MONTH, 15);
+// c.set(Calendar.HOUR_OF_DAY, 0);
+// c.set(Calendar.MINUTE, 0);
+// c.set(Calendar.SECOND, 0);
+// c.set(Calendar.MILLISECOND, 0);
+// long START_EPOCH = c.getTimeInMillis();
\ No newline at end of file