/* * Copyright (c) 2011, 2012, 2015, 2016 Eike Stepper (Berlin, Germany) and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Eike Stepper - initial API and implementation */ package org.eclipse.net4j.util; import java.lang.reflect.Method; import java.net.NetworkInterface; import java.net.SocketException; import java.security.SecureRandom; import java.util.Enumeration; import java.util.GregorianCalendar; import java.util.Random; /** * Generates 16 byte UUID values and can encode them to Strings, decode from Strings respectively. * * @author Eike Stepper * @since 3.2 */ public final class UUIDGenerator { public static final int NODE_ADDRESS_BYTES = 6; public static final UUIDGenerator DEFAULT = new UUIDGenerator(); public UUIDGenerator(byte[] nodeAddress) { Random random = new SecureRandom(); clockSequence = (short)random.nextInt(16384); updateClockSequence(); if (nodeAddress == null) { try { nodeAddress = getHardwareAddress(); } catch (Throwable ex) { //$FALL-THROUGH$ } if (nodeAddress == null || nodeAddress.length != NODE_ADDRESS_BYTES) { // Generate a 48 bit node identifier; // This is an alternative to the IEEE 802 host address, which is not available in Java. nodeAddress = new byte[NODE_ADDRESS_BYTES]; random.nextBytes(nodeAddress); } } setNodeAddress(nodeAddress); } public UUIDGenerator() { this(null); } public synchronized String generate() { updateCurrentTime(); encode(uuid, buffer); return new String(buffer); } public synchronized void generate(byte[] uuid) { updateCurrentTime(); for (int i = 0; i < 16; i++) { uuid[i] = this.uuid[i]; } } public String encode(byte[] uuid) { char[] buffer = createBuffer(); encode(uuid, buffer); return new String(buffer); } public byte[] decode(String string) { byte[] uuid = createUUID(); char c1; char c2; char c3; char c4; int i1; int i2; int i3; int i4; for (int i = 0; i < 5; ++i) { c1 = string.charAt(4 * i + 1); c2 = string.charAt(4 * i + 2); c3 = string.charAt(4 * i + 3); c4 = string.charAt(4 * i + 4); i1 = BASE64_INDEX[c1 - BASE64_INDEX_OFFSET]; i2 = BASE64_INDEX[c2 - BASE64_INDEX_OFFSET]; i3 = BASE64_INDEX[c3 - BASE64_INDEX_OFFSET]; i4 = BASE64_INDEX[c4 - BASE64_INDEX_OFFSET]; uuid[3 * i] = (byte)(i1 << 2 | i2 >>> 4); uuid[3 * i + 1] = (byte)((i2 & 0xF) << 4 | i3 >>> 2); uuid[3 * i + 2] = (byte)((i3 & 0x3) << 6 | i4); } // Handle the last chars at the end. // c1 = string.charAt(21); c2 = string.charAt(22); i1 = BASE64_INDEX[c1 - BASE64_INDEX_OFFSET]; i2 = BASE64_INDEX[c2 - BASE64_INDEX_OFFSET]; uuid[15] = (byte)(i1 << 2 | i2 >>> 4); return uuid; } private static final char[] BASE64_DIGITS = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' }; private static final byte[] BASE64_INDEX; private static final int BASE64_INDEX_OFFSET; /** * An adjustment to convert the Java epoch of Jan 1, 1970 00:00:00 to the epoch required by the IETF specification, * Oct 15, 1582 00:00:00. */ private static final long EPOCH_ADJUSTMENT = new GregorianCalendar(1970, 0, 1, 0, 0, 0).getTime().getTime() - new GregorianCalendar(1582, 9, 15, 0, 0, 0).getTime().getTime(); private long lastTime = System.currentTimeMillis() + EPOCH_ADJUSTMENT; private short clockSequence; private short timeAdjustment; private int sleepTime = 1; private final char[] buffer = createBuffer(); /** * A cached array of bytes representing the UUID. The second 8 bytes will be kept the same unless the clock sequence * has changed. */ private final byte[] uuid = createUUID(); static { byte[] index = new byte[256]; int min = Integer.MAX_VALUE; int max = Integer.MIN_VALUE; for (byte i = 0; i < BASE64_DIGITS.length; i++) { char digit = BASE64_DIGITS[i]; if (digit < min) { min = digit; } if (digit > max) { max = digit; } index[digit] = i; } int length = max - min + 1; BASE64_INDEX = new byte[length]; BASE64_INDEX_OFFSET = min; System.arraycopy(index, BASE64_INDEX_OFFSET, BASE64_INDEX, 0, length); } private byte[] getHardwareAddress() throws Throwable { // getHardwareAddress is a JRE 1.6 method and must be called reflectiviely Method method = ReflectUtil.getMethod(NetworkInterface.class, "getHardwareAddress"); Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces(); while (networkInterfaces.hasMoreElements()) { NetworkInterface networkInterface = networkInterfaces.nextElement(); try { byte[] nodeAddress = (byte[])ReflectUtil.invokeMethod(method, networkInterface); if (nodeAddress != null && nodeAddress.length == NODE_ADDRESS_BYTES) { return nodeAddress; } } catch (Throwable ex) { //$FALL-THROUGH$ } } throw new SocketException("Hardware address could not be determined"); } private void setNodeAddress(byte[] nodeAddress) { // Set the most significant bit of the first octet to 1 so as to distinguish it from IEEE node addresses // nodeAddress[0] |= (byte)0x80; // The node identifier is already in network byte order, // so there is no need to do any byte order reversing. // for (int i = 0; i < NODE_ADDRESS_BYTES; ++i) { uuid[i + 10] = nodeAddress[i]; } } /** * Updates the clock sequence portion of the UUID. The clock sequence portion may seem odd, but in the specification, * the high order byte comes before the low order byte. The variant is multiplexed into the high order octet of * clockseq_hi. */ private void updateClockSequence() { // clockseq_hi uuid[8] = (byte)(clockSequence >> 8 & 0x3F | 0x80); // clockseq_low uuid[9] = (byte)(clockSequence & 0xFF); } /** * Updates the UUID with the current time, compensating for the fact that the clock resolution may be less than 100 * ns. The byte array will have its first eight bytes populated with the time in the correct sequence of bytes, as per * the specification. */ private void updateCurrentTime() { // Get the current time in milliseconds since the epoch // and adjust it to match the epoch required by the specification. // long currentTime = System.currentTimeMillis() + EPOCH_ADJUSTMENT; if (lastTime > currentTime) { // The system clock has been rewound so the clock sequence must be incremented // to ensure that a duplicate UUID is not generated. // ++clockSequence; if (16384 == clockSequence) { clockSequence = 0; } updateClockSequence(); } else if (lastTime == currentTime) { // The system time hasn't changed so add some increment of 100s of nanoseconds to guarantee uniqueness. // ++timeAdjustment; if (timeAdjustment > 9999) { // Wait so that the clock can catch up and the time adjustment won't overflow. try { Thread.sleep(sleepTime); } catch (InterruptedException exception) { // We just woke up. } timeAdjustment = 0; currentTime = System.currentTimeMillis() + EPOCH_ADJUSTMENT; while (lastTime == currentTime) { try { ++sleepTime; Thread.sleep(1); } catch (InterruptedException exception) { // We just woke up. } currentTime = System.currentTimeMillis() + EPOCH_ADJUSTMENT; } } } else { timeAdjustment = 0; } lastTime = currentTime; // Since the granularity of time in Java is only milliseconds, // add an adjustment so that the time is represented in 100s of nanoseconds. // The version number (1) is multiplexed into the most significant hex digit. // currentTime *= 10000; currentTime += timeAdjustment; currentTime |= 0x1000000000000000L; // Place the time into the byte array in network byte order. // for (int i = 0; i < 4; ++i) { // time_low // uuid[i] = (byte)(currentTime >> 8 * (3 - i) & 0xFFL); } for (int i = 0; i < 2; ++i) { // time_mid // uuid[i + 4] = (byte)(currentTime >> 8 * (1 - i) + 32 & 0xFFL); } for (int i = 0; i < 2; ++i) { // time_hi // uuid[i + 6] = (byte)(currentTime >> 8 * (1 - i) + 48 & 0xFFL); } } private void encode(byte[] uuid, char[] buffer) { for (int i = 0; i < 5; ++i) { buffer[4 * i + 1] = BASE64_DIGITS[uuid[i * 3] >> 2 & 0x3F]; buffer[4 * i + 2] = BASE64_DIGITS[uuid[i * 3] << 4 & 0x30 | uuid[i * 3 + 1] >> 4 & 0xF]; buffer[4 * i + 3] = BASE64_DIGITS[uuid[i * 3 + 1] << 2 & 0x3C | uuid[i * 3 + 2] >> 6 & 0x3]; buffer[4 * i + 4] = BASE64_DIGITS[uuid[i * 3 + 2] & 0x3F]; } // Handle the last byte at the end. // buffer[21] = BASE64_DIGITS[uuid[15] >> 2 & 0x3F]; buffer[22] = BASE64_DIGITS[uuid[15] << 4 & 0x30]; } private byte[] createUUID() { return new byte[16]; } private char[] createBuffer() { char[] buffer = new char[23]; buffer[0] = '_'; return buffer; } }