| /* |
| * 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. |
| */ |
| package org.apache.openjpa.lib.util; |
| |
| import java.io.IOException; |
| import java.net.InetAddress; |
| import java.security.SecureRandom; |
| import java.util.Random; |
| import java.util.UUID; |
| |
| |
| /** |
| * UUID value generator. Type 1 generator is based on the time-based generator |
| * in the Apache Commons Id project: http://jakarta.apache.org/commons/sandbox |
| * /id/uuid.html The type 4 generator uses the standard Java UUID generator. |
| * |
| * The type 1 code has been vastly simplified and modified to replace the |
| * ethernet address of the host machine with the IP, since we do not want to |
| * require native libs and Java cannot access the MAC address directly. |
| * |
| * In spirit, implements the IETF UUID draft specification, found here:<br /> |
| * http://www1.ics.uci.edu/~ejw/authoring/uuid-guid/draft-leach-uuids-guids-01 |
| * .txt |
| * |
| * @author Abe White, Kevin Sutter |
| * @since 0.3.3 |
| */ |
| public class UUIDGenerator { |
| |
| // supported UUID types |
| public static final int TYPE1 = 1; |
| public static final int TYPE4 = 4; |
| |
| // indexes within the uuid array for certain boundaries |
| private static final byte IDX_TIME_HI = 6; |
| private static final byte IDX_TYPE = 6; // multiplexed |
| private static final byte IDX_TIME_MID = 4; |
| private static final byte IDX_TIME_LO = 0; |
| private static final byte IDX_TIME_SEQ = 8; |
| private static final byte IDX_VARIATION = 8; // multiplexed |
| |
| // indexes and lengths within the timestamp for certain boundaries |
| private static final byte TS_TIME_LO_IDX = 4; |
| private static final byte TS_TIME_LO_LEN = 4; |
| private static final byte TS_TIME_MID_IDX = 2; |
| private static final byte TS_TIME_MID_LEN = 2; |
| private static final byte TS_TIME_HI_IDX = 0; |
| private static final byte TS_TIME_HI_LEN = 2; |
| |
| // offset to move from 1/1/1970, which is 0-time for Java, to gregorian |
| // 0-time 10/15/1582, and multiplier to go from 100nsec to msec units |
| private static final long GREG_OFFSET = 0xB1D069B5400L; |
| private static final long MILLI_MULT = 10000L; |
| |
| // type of UUID -- time based |
| private final static byte TYPE_TIME_BASED = 0x10; |
| |
| // random number generator used to reduce conflicts with other JVMs, and |
| // hasher for strings. |
| private static Random RANDOM; |
| |
| // 4-byte IP address + 2 random bytes to compensate for the fact that |
| // the MAC address is usually 6 bytes |
| private static byte[] IP; |
| |
| // counter is initialized to 0 and is incremented for each uuid request |
| // within the same timestamp window. |
| private static int _counter; |
| |
| // current timestamp (used to detect multiple uuid requests within same |
| // timestamp) |
| private static long _currentMillis; |
| |
| // last used millis time, and a semi-random sequence that gets reset |
| // when it overflows |
| private static long _lastMillis = 0L; |
| private static final int MAX_14BIT = 0x3FFF; |
| private static short _seq = 0; |
| |
| private static boolean type1Initialized = false; |
| /* |
| * Initializer for type 1 UUIDs. Creates random generator and genenerates |
| * the node portion of the UUID using the IP address. |
| */ |
| private static synchronized void initializeForType1() { |
| if (type1Initialized == true) { |
| return; |
| } |
| // note that secure random is very slow the first time |
| // it is used; consider switching to a standard random |
| RANDOM = new SecureRandom(); |
| _seq = (short) RANDOM.nextInt(MAX_14BIT); |
| |
| byte[] ip = null; |
| try { |
| ip = InetAddress.getLocalHost().getAddress(); |
| } catch (IOException ioe) { |
| throw new RuntimeException(ioe); |
| } |
| |
| IP = new byte[6]; |
| RANDOM.nextBytes(IP); |
| |
| //OPENJPA-2055: account for the fact that 'getAddress' |
| //may return an IPv6 address which is 16 bytes wide. |
| for( int i = 0 ; i < ip.length; ++i ) { |
| IP[2+(i%4)] ^= ip[i]; |
| } |
| |
| type1Initialized = true; |
| } |
| |
| /** |
| * Return a unique UUID value. |
| */ |
| public static byte[] next(int type) { |
| if (type == TYPE4) { |
| return createType4(); |
| } |
| return createType1(); |
| } |
| |
| /* |
| * Creates a type 1 UUID |
| */ |
| public static byte[] createType1() { |
| if (type1Initialized == false) { |
| initializeForType1(); |
| } |
| // set ip addr |
| byte[] uuid = new byte[16]; |
| System.arraycopy(IP, 0, uuid, 10, IP.length); |
| |
| // Set time info. Have to do this processing within a synchronized |
| // block because of the statics... |
| long now = 0; |
| synchronized (UUIDGenerator.class) { |
| // Get the time to use for this uuid. This method has the side |
| // effect of modifying the clock sequence, as well. |
| now = getTime(); |
| |
| // Insert the resulting clock sequence into the uuid |
| uuid[IDX_TIME_SEQ] = (byte) ((_seq & 0x3F00) >>> 8); |
| uuid[IDX_VARIATION] |= 0x80; |
| uuid[IDX_TIME_SEQ+1] = (byte) (_seq & 0xFF); |
| |
| } |
| |
| // have to break up time because bytes are spread through uuid |
| byte[] timeBytes = Bytes.toBytes(now); |
| |
| // Copy time low |
| System.arraycopy(timeBytes, TS_TIME_LO_IDX, uuid, IDX_TIME_LO, |
| TS_TIME_LO_LEN); |
| // Copy time mid |
| System.arraycopy(timeBytes, TS_TIME_MID_IDX, uuid, IDX_TIME_MID, |
| TS_TIME_MID_LEN); |
| // Copy time hi |
| System.arraycopy(timeBytes, TS_TIME_HI_IDX, uuid, IDX_TIME_HI, |
| TS_TIME_HI_LEN); |
| //Set version (time-based) |
| uuid[IDX_TYPE] |= TYPE_TIME_BASED; // 0001 0000 |
| |
| return uuid; |
| } |
| |
| /* |
| * Creates a type 4 UUID |
| */ |
| private static byte[] createType4() { |
| UUID type4 = UUID.randomUUID(); |
| byte[] uuid = new byte[16]; |
| longToBytes(type4.getMostSignificantBits(), uuid, 0); |
| longToBytes(type4.getLeastSignificantBits(), uuid, 8); |
| return uuid; |
| } |
| |
| /* |
| * Converts a long to byte values, setting them in a byte array |
| * at a given starting position. |
| */ |
| private static void longToBytes(long longVal, byte[] buf, int sPos) { |
| sPos += 7; |
| for(int i = 0; i < 8; i++) |
| buf[sPos-i] = (byte)(longVal >>> (i * 8)); |
| } |
| |
| /** |
| * Return the next unique uuid value as a 16-character string. |
| */ |
| public static String nextString(int type) { |
| byte[] bytes = next(type); |
| try { |
| return new String(bytes, "ISO-8859-1"); |
| } catch (Exception e) { |
| return new String(bytes); |
| } |
| } |
| |
| /** |
| * Return the next unique uuid value as a 32-character hex string. |
| */ |
| public static String nextHex(int type) { |
| return Base16Encoder.encode(next(type)); |
| } |
| |
| /** |
| * Get the timestamp to be used for this uuid. Must be called from |
| * a synchronized block. |
| * |
| * @return long timestamp |
| */ |
| // package-visibility for testing |
| static long getTime() { |
| if (RANDOM == null) |
| initializeForType1(); |
| long newTime = getUUIDTime(); |
| if (newTime <= _lastMillis) { |
| incrementSequence(); |
| newTime = getUUIDTime(); |
| } |
| _lastMillis = newTime; |
| return newTime; |
| } |
| |
| /** |
| * Gets the appropriately modified timestamep for the UUID. Must be called |
| * from a synchronized block. |
| * |
| * @return long timestamp in 100ns intervals since the Gregorian change |
| * offset |
| */ |
| private static long getUUIDTime() { |
| if (_currentMillis != System.currentTimeMillis()) { |
| _currentMillis = System.currentTimeMillis(); |
| _counter = 0; // reset counter |
| } |
| |
| // check to see if we have created too many uuid's for this timestamp |
| if (_counter + 1 >= MILLI_MULT) { |
| // Original algorithm threw exception. Seemed like overkill. |
| // Let's just increment the timestamp instead and start over... |
| _currentMillis++; |
| _counter = 0; |
| } |
| |
| // calculate time as current millis plus offset times 100 ns ticks |
| long currentTime = (_currentMillis + GREG_OFFSET) * MILLI_MULT; |
| |
| // return the uuid time plus the artificial tick counter incremented |
| return currentTime + _counter++; |
| } |
| |
| /** |
| * Increments the clock sequence for this uuid. Must be called from a |
| * synchronized block. |
| */ |
| private static void incrementSequence() { |
| // increment, but if it's greater than its 14-bits, reset it |
| if (++_seq > MAX_14BIT) { |
| _seq = (short) RANDOM.nextInt(MAX_14BIT); // semi-random |
| } |
| } |
| |
| |
| } |