PageRenderTime 49ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/src/java/org/apache/cassandra/utils/UUIDGen.java

https://github.com/thepaul/cassandra
Java | 262 lines | 148 code | 32 blank | 82 comment | 13 complexity | ada79b95c4beb29790571ca11e959a6c MD5 | raw file
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. package org.apache.cassandra.utils;
  19. import java.net.InetAddress;
  20. import java.nio.ByteBuffer;
  21. import java.security.MessageDigest;
  22. import java.security.NoSuchAlgorithmException;
  23. import java.util.HashMap;
  24. import java.util.Map;
  25. import java.util.Random;
  26. import java.util.UUID;
  27. /**
  28. * The goods are here: www.ietf.org/rfc/rfc4122.txt.
  29. */
  30. public class UUIDGen
  31. {
  32. // A grand day! millis at 00:00:00.000 15 Oct 1582.
  33. private static final long START_EPOCH = -12219292800000L;
  34. private static final long clock = new Random(System.currentTimeMillis()).nextLong();
  35. // placement of this singleton is important. It needs to be instantiated *AFTER* the other statics.
  36. private static final UUIDGen instance = new UUIDGen();
  37. private long lastNanos;
  38. private final Map<InetAddress, Long> nodeCache = new HashMap<InetAddress, Long>();
  39. private static final ThreadLocal<MessageDigest> localMD5Digest = new ThreadLocal<MessageDigest>()
  40. {
  41. @Override
  42. protected MessageDigest initialValue()
  43. {
  44. try
  45. {
  46. return MessageDigest.getInstance("MD5");
  47. }
  48. catch (NoSuchAlgorithmException nsae)
  49. {
  50. throw new RuntimeException("MD5 digest algorithm is not available", nsae);
  51. }
  52. }
  53. @Override
  54. public MessageDigest get()
  55. {
  56. MessageDigest digest = super.get();
  57. digest.reset();
  58. return digest;
  59. }
  60. };
  61. private UUIDGen()
  62. {
  63. // make sure someone didn't whack the clock by changing the order of instantiation.
  64. if (clock == 0) throw new RuntimeException("singleton instantiation is misplaced.");
  65. }
  66. /**
  67. * Creates a type 1 UUID (time-based UUID) that substitutes a hash of
  68. * an IP address in place of the MAC (unavailable to Java).
  69. *
  70. * @param addr the host address to use
  71. * @return a UUID instance
  72. */
  73. public static UUID makeType1UUIDFromHost(InetAddress addr)
  74. {
  75. return new UUID(instance.createTimeSafe(), instance.getClockSeqAndNode(addr));
  76. }
  77. /** creates a type 1 uuid from raw bytes. */
  78. public static UUID getUUID(ByteBuffer raw)
  79. {
  80. return new UUID(raw.getLong(raw.position()), raw.getLong(raw.position() + 8));
  81. }
  82. /** decomposes a uuid into raw bytes. */
  83. public static byte[] decompose(UUID uuid)
  84. {
  85. long most = uuid.getMostSignificantBits();
  86. long least = uuid.getLeastSignificantBits();
  87. byte[] b = new byte[16];
  88. for (int i = 0; i < 8; i++)
  89. {
  90. b[i] = (byte)(most >>> ((7-i) * 8));
  91. b[8+i] = (byte)(least >>> ((7-i) * 8));
  92. }
  93. return b;
  94. }
  95. /**
  96. * Returns a 16 byte representation of a type 1 UUID (a time-based UUID),
  97. * based on the current system time.
  98. *
  99. * @return a type 1 UUID represented as a byte[]
  100. */
  101. public static byte[] getTimeUUIDBytes()
  102. {
  103. return createTimeUUIDBytes(instance.createTimeSafe());
  104. }
  105. /**
  106. * Converts a milliseconds-since-epoch timestamp into the 16 byte representation
  107. * of a type 1 UUID (a time-based UUID).
  108. *
  109. * <p><i><b>Warning:</b> This method is not guaranteed to return unique UUIDs; Multiple
  110. * invocations using identical timestamps will result in identical UUIDs.</i></p>
  111. *
  112. * @param timeMillis
  113. * @return a type 1 UUID represented as a byte[]
  114. */
  115. public static byte[] getTimeUUIDBytes(long timeMillis)
  116. {
  117. return createTimeUUIDBytes(instance.createTimeUnsafe(timeMillis));
  118. }
  119. /**
  120. * Converts a 100-nanoseconds precision timestamp into the 16 byte representation
  121. * of a type 1 UUID (a time-based UUID).
  122. *
  123. * To specify a 100-nanoseconds precision timestamp, one should provide a milliseconds timestamp and
  124. * a number 0 <= n < 10000 such that n*100 is the number of nanoseconds within that millisecond.
  125. *
  126. * <p><i><b>Warning:</b> This method is not guaranteed to return unique UUIDs; Multiple
  127. * invocations using identical timestamps will result in identical UUIDs.</i></p>
  128. *
  129. * @return a type 1 UUID represented as a byte[]
  130. */
  131. public static byte[] getTimeUUIDBytes(long timeMillis, int nanos)
  132. {
  133. if (nanos >= 10000)
  134. throw new IllegalArgumentException();
  135. return createTimeUUIDBytes(instance.createTimeUnsafe(timeMillis, nanos));
  136. }
  137. private static byte[] createTimeUUIDBytes(long msb)
  138. {
  139. long lsb = instance.getClockSeqAndNode(FBUtilities.getLocalAddress());
  140. byte[] uuidBytes = new byte[16];
  141. for (int i = 0; i < 8; i++)
  142. uuidBytes[i] = (byte) (msb >>> 8 * (7 - i));
  143. for (int i = 8; i < 16; i++)
  144. uuidBytes[i] = (byte) (lsb >>> 8 * (7 - i));
  145. return uuidBytes;
  146. }
  147. /**
  148. * Returns a milliseconds-since-epoch value for a type-1 UUID.
  149. *
  150. * @param uuid a type-1 (time-based) UUID
  151. * @return the number of milliseconds since the unix epoch
  152. * @throws IllegalArgumentException if the UUID is not version 1
  153. */
  154. public static long getAdjustedTimestamp(UUID uuid)
  155. {
  156. if (uuid.version() != 1)
  157. throw new IllegalArgumentException("incompatible with uuid version: "+uuid.version());
  158. return (uuid.timestamp() / 10000) - START_EPOCH;
  159. }
  160. // todo: could cache value if we assume node doesn't change.
  161. private long getClockSeqAndNode(InetAddress addr)
  162. {
  163. long lsb = 0;
  164. lsb |= (clock & 0x3f00000000000000L) >>> 56; // was 58?
  165. lsb |= 0x0000000000000080;
  166. lsb |= (clock & 0x00ff000000000000L) >>> 48;
  167. lsb |= makeNode(addr);
  168. return lsb;
  169. }
  170. // needs to return two different values for the same when.
  171. // we can generate at most 10k UUIDs per ms.
  172. private synchronized long createTimeSafe()
  173. {
  174. long nanosSince = (System.currentTimeMillis() - START_EPOCH) * 10000;
  175. if (nanosSince > lastNanos)
  176. lastNanos = nanosSince;
  177. else
  178. nanosSince = ++lastNanos;
  179. return createTime(nanosSince);
  180. }
  181. private long createTimeUnsafe(long when)
  182. {
  183. return createTimeUnsafe(when, 0);
  184. }
  185. private long createTimeUnsafe(long when, int nanos)
  186. {
  187. long nanosSince = ((when - START_EPOCH) * 10000) + nanos;
  188. return createTime(nanosSince);
  189. }
  190. private long createTime(long nanosSince)
  191. {
  192. long msb = 0L;
  193. msb |= (0x00000000ffffffffL & nanosSince) << 32;
  194. msb |= (0x0000ffff00000000L & nanosSince) >>> 16;
  195. msb |= (0xffff000000000000L & nanosSince) >>> 48;
  196. msb |= 0x0000000000001000L; // sets the version to 1.
  197. return msb;
  198. }
  199. // Lazily create node hashes, and cache them for later
  200. private long makeNode(InetAddress addr)
  201. {
  202. if (nodeCache.containsKey(addr))
  203. return nodeCache.get(addr);
  204. // ideally, we'd use the MAC address, but java doesn't expose that.
  205. byte[] hash = hash(addr.toString());
  206. long node = 0;
  207. for (int i = 0; i < Math.min(6, hash.length); i++)
  208. node |= (0x00000000000000ff & (long)hash[i]) << (5-i)*8;
  209. assert (0xff00000000000000L & node) == 0;
  210. nodeCache.put(addr, node);
  211. return node;
  212. }
  213. private static byte[] hash(String... data)
  214. {
  215. MessageDigest messageDigest = localMD5Digest.get();
  216. for(String block : data)
  217. messageDigest.update(block.getBytes());
  218. return messageDigest.digest();
  219. }
  220. }
  221. // for the curious, here is how I generated START_EPOCH
  222. // Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT-0"));
  223. // c.set(Calendar.YEAR, 1582);
  224. // c.set(Calendar.MONTH, Calendar.OCTOBER);
  225. // c.set(Calendar.DAY_OF_MONTH, 15);
  226. // c.set(Calendar.HOUR_OF_DAY, 0);
  227. // c.set(Calendar.MINUTE, 0);
  228. // c.set(Calendar.SECOND, 0);
  229. // c.set(Calendar.MILLISECOND, 0);
  230. // long START_EPOCH = c.getTimeInMillis();