com.day.util.UUID Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aem-sdk-api Show documentation
Show all versions of aem-sdk-api Show documentation
The Adobe Experience Manager SDK
/*************************************************************************
* ADOBE CONFIDENTIAL
* __________________
*
* Copyright 2020 Adobe
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of Adobe and its suppliers, if any. The intellectual
* and technical concepts contained herein are proprietary to Adobe
* and its suppliers and are protected by all applicable intellectual
* property laws, including trade secret and copyright laws.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Adobe.
**************************************************************************/
package com.day.util;
import java.io.*;
import java.net.InetAddress;
import java.security.MessageDigest;
import java.util.*;
/**
* A Universally Unique Identifier (UUID) is a 128 bit number generated
* according to an algorithm that is garanteed to be unique in time and
* space from all other UUIDs. It consists of an IEEE 802 Internet Address
* and various time stamps to ensure uniqueness. For a complete
* specification, see
*
* http://www.ietf.org/internet-drafts/draft-mealling-uuid-urn-05.txt
*
* @version $Revision: 1.21 $
* @author tripod
* @since antbear
* Audience wad
*/
public class UUID implements Serializable {
/** cached bytes */
private final byte[] bytes;
/** cached hashCode */
private int hashCode;
/** the version */
private final int version;
/** hexdigits for toString */
public static final char[] hexDigits = "0123456789abcdef".toCharArray();
/**
* Generates a UUID from a string. the string must have the form. the version
* is set accoridingly.
* "00000000-0000-0000-0000-000000000000"
* @param string the string to use
*/
public UUID(String string) {
// convert the string to bytes
if (string.length() > 36) {
// cut of, if bigger than 36. actually, we should throw an
// IllegalArgumentException, but we are affraid of backward
// compatability issues (bug #9612)
string = string.substring(0, 36);
}
this.bytes = new byte[16];
for (int i=0, j=0; i<32+4; i+=2) {
bytes[j++]=(byte) Integer.parseInt(string.substring(i,i+2), 16);
if (i==6 || i==11 || i==16 || i==21) {
i++;
}
}
// grab out version
this.version=(bytes[7]>>4)&0x0f;
}
/**
* Creates a UUID from a byte array.
* @param bytes the byte array
*/
public UUID(byte[] bytes) {
this.bytes = new byte[16];
System.arraycopy(bytes, 0, this.bytes, 0 , java.lang.Math.min(16, bytes.length));
// grab out version
this.version=(bytes[7]>>4)&0x0f;
}
/**
* Creates a UUID from 2 longs
* @param longs the 2 longs
*/
public UUID(long[] longs) {
long hi = longs[0];
long lo = longs[1];
bytes = new byte[16];
for (int i=7; i>=0; i--) {
bytes[i] = (byte) (hi&0xff);
hi>>=8;
}
for (int i=7; i>=0; i--) {
bytes[8+i] = (byte) (lo&0xff);
lo>>=8;
}
// grab out version
this.version=(bytes[7]>>4)&0x0f;
}
/**
* Create a name base UUID (version 3)
*/
public UUID(String name, String namespace) {
version = 3;
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(namespace.getBytes());
md5.update(name.getBytes());
bytes = md5.digest();
// tag version and reserved
bytes[7] = (byte) ((bytes[7] & 0x0f) | (byte) (version << 4));
bytes[8] = (byte) ((bytes[8] & 0x3f) | 0x80);
return;
} catch (Exception exc) {
throw new InternalError("MD5 not available");
}
}
/**
* Generates a random UUID (version 4)
*/
public UUID() {
// todo: implement using a cryptographic random generator
this.version = 4;
// tag version and reserved
try {
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(byteOut);
out.writeLong(System.currentTimeMillis());
out.writeInt(Thread.currentThread().hashCode());
out.write(internetAddress);
out.writeInt(random());
out.flush();
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(byteOut.toByteArray());
bytes = md5.digest();
// tag version and reserved
bytes[7] = (byte) ((bytes[7] & 0x0f) | (byte) (version << 4));
bytes[8] = (byte) ((bytes[8] & 0x3f) | 0x80);
return;
} catch (Exception exc) {
throw new InternalError("MD5 not available");
}
}
/**
* Generate a time-based UUID for this host (version 1).
*
*
* Field Data Type Octet# Note
* time_low unsigned 32 0-3 The low field of the
* bit integer timestamp.
*
* time_mid unsigned 16 4-5 The middle field of the
* bit integer timestamp.
*
* time_hi_and_version unsigned 16 6-7 The high field of the
* bit integer timestamp multiplexed
* with the version number.
*
* clock_seq_hi_and_rese unsigned 8 8 The high field of the
* rved bit integer clock sequence
* multiplexed with the
* variant.
*
* clock_seq_low unsigned 8 9 The low field of the
* bit integer clock sequence.
*
* node unsigned 48 10-15 The spatially unique
* bit integer node identifier.
*
* @param node the node
* @param time the time
* @param clock the clock
*/
public UUID(byte[] node, long time, short clock) {
version = 1;
bytes = new byte[16];
long t = time;
for (int i = 0; i < 8; i++) {
bytes[i] = (byte) (t & 0xFF);
t>>=8;
}
bytes[7] |= (byte) (version << 4); // time hi and version
bytes[8] = (byte) ((clock>>8 & 0x3f) | 0x80);
bytes[9] = (byte) (clock & 0xFF);
for (int i = 0; i < 6; i++) {
bytes[10 + i] = node[i]; // node
}
}
/**
* Get the UUID version number.
* @return the version
*/
public int getVersion() {
return version;
}
/**
* Compare two UUIDs
* @return true
if the UUIDs are equal;
* false
otherwise.
*/
public boolean equals(Object toUUID) {
if (this == toUUID) {
return true;
}
if (toUUID instanceof UUID) {
UUID uuid = (UUID) toUUID;
if (this.bytes == uuid.bytes) {
return true;
}
for (int i=0; i<16; i++) {
if (this.bytes[i]!=uuid.bytes[i]) {
return false;
}
}
return true;
}
return false;
}
/**
* Provide a String representation of a UUID as specified in section
* 3.5 of [leach]. it has this format:
* "00000000-0000-0000-0000-000000000000"
*/
public String toString() {
char[] chars = new char[32 + 4];
for (int i = 0, j = 0; i < 16; i++) {
chars[j++] = hexDigits[(bytes[i] >> 4) & 0x0f];
chars[j++] = hexDigits[bytes[i] & 0x0f];
if (i == 3 || i == 5 || i == 7 || i == 9) {
chars[j++] = '-';
}
}
return new String(chars);
}
/**
* Returns the bytes of the uuid
* @return the bytes
*/
public byte[] getBytes() {
return bytes;
}
/**
* Returns a hash code value for the object. This method is
* supported for the benefit of hashtables such as those provided by
* java.util.Hashtable
.
*
* @return a hash code value for this object.
* @see java.lang.Object#equals(java.lang.Object)
* @see java.util.Hashtable
*/
public int hashCode() {
int h = hashCode;
if (h == 0) {
for (int i = 0; i < bytes.length; i++) {
h = 31*h + bytes[i];
}
hashCode = h;
}
return hashCode;
}
//---------------------------------------------< static generator stuff >---
/**
* the internet address of this machine. actually a MAC address would be
* better.
*/
private static byte[] internetAddress = null;
/** File to store the last generated UUID */
private static File uuidFile = null;
/** upper bound for uuid chunks */
private static final int UUIDsPerTick = 128;
/** time when last uuid was generated */
private static long lastTime = new Date().getTime();
/** milliseconds since 15.10.1582 */
private static final long gregorianOffset =
- (new GregorianCalendar(1582, 9, 15).getTime().getTime());
/** sub-ticks */
private static int uuidsThisTick = UUIDsPerTick;
/** last generated uuid. initialized from saved state */
private static short prevClock = 0;
private static long prevTime = 0;
private static byte[] prevNode = null;
/** time when the next uuid has to be stored */
private static long nextSave = new Date().getTime();
/** the reandom generator for fake IEEE address */
private static Random randomGenerator = new Random(new Date().getTime());
/**
* Initialize the UUID generator.
* @param uuidStateFile the to read/write the state from
*/
public static void init(File uuidStateFile) {
try {
internetAddress = InetAddress.getLocalHost().getAddress();
} catch (Exception exc) {
throw new InternalError("Unable to get host address: " + exc);
}
// initialize the last state
uuidFile = uuidStateFile;
loadState();
}
/**
* Generate a UUID for this host using version 1 of [leach]
* @return the UUID
*/
public synchronized static UUID create() {
long time = getCurrentTime();
short clock = prevClock;
byte[] node = prevNode;
// if saved time is newer (i.e. clock is set backwards)
if (prevTime > time) {
clock++;
}
UUID uuid = new UUID(node, time, clock);
// save for the next UUID
saveState(time, clock, node);
return uuid;
}
/**
* Get a 48 bit cryptographic quality random number to use as the node field
* of a UUID as specified in section 6.4.1 of version 10 of the WebDAV spec.
* This is an alternative to the IEEE 802 host address which is not available
* from Java. The number will not conflict with any IEEE 802 host address
* because the most significant bit of the first octet is set to 1.
*
* @return a 48 bit number specifying an id for this node
*/
private static byte[] computeNodeAddress() {
byte[] address = new byte[6];
// create a random number by concatenating:
// the hash code for the current thread
// the current time in milli-seconds
// the internet address for this node
int thread = Thread.currentThread().hashCode();
long time = System.currentTimeMillis();
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(byteOut);
try {
if (internetAddress != null) {
out.write(internetAddress);
}
out.write(thread);
out.writeLong(time);
out.close();
} catch (IOException exc) {
}
byte[] rand = byteOut.toByteArray();
MessageDigest md5;
try {
md5 = MessageDigest.getInstance("MD5");
} catch (Exception exc) {
throw new InternalError(exc.toString());
}
md5.update(rand);
byte[] temp = md5.digest();
// pick the middle 6 bytes of the MD5 digest
for (int i = 0; i < 6; i++) {
address[i] = temp[i + 5];
}
// set the MSB of the first octet to 1 to distinguish from IEEE node addresses
address[0] = (byte) (address[0] | (byte) 0x80);
return address;
}
/**
* Get the current time compensating for the fact that the real
* clock resolution may be less than 100ns.
*
* @return the current date and time
*/
private synchronized static long getCurrentTime() {
long now = 0;
boolean waitForTick = true;
while (waitForTick) {
now = (new Date().getTime()+gregorianOffset)*10; // adjust to 100ns
if (lastTime < now) {
// got a new tick, make sure uuidsPerTick doesn't cause an overrun
uuidsThisTick = 0;
waitForTick = false;
} else if (uuidsThisTick < UUIDsPerTick) {
// if we are faster than clock, just use next tick
uuidsThisTick++;
waitForTick = false;
}
}
// add the uuidsThisTick to the time to increase the clock resolution
now += uuidsThisTick;
lastTime = now;
return now;
}
/**
* Get the 48 bit IEEE 802 host address. NOT IMPLEMENTED
* @return a 48 bit number specifying a unique location
*/
private static byte[] getIEEEAddress() {
byte[] address = new byte[6];
// TODO: get the IEEE 802 host address
return address;
}
/**
* Generate a crypto-quality random number. This implementation
* doesn't do that.
* @return a random number
*/
private static int random() {
return randomGenerator.nextInt();
}
/**
* Loads the UUID generator state. This consists of the last (or nearly
* last) UUID generated. This state is used in the construction of the next
* UUID.
*/
private static void loadState() {
try {
ObjectInputStream s = new ObjectInputStream(
new FileInputStream(uuidFile)
);
prevTime = s.readLong();
prevClock = s.readShort();
prevNode = new byte[6];
s.readFully(prevNode);
s.close();
} catch (Exception exc) {
prevNode = computeNodeAddress();
prevTime = 0;
prevClock = (short) random();
}
}
/**
* Set the persistent UUID state.
*/
private static void saveState(long time, short clock, byte[] node) {
prevTime = time;
prevClock = clock;
prevNode = node;
if (prevTime > nextSave) {
try {
ObjectOutputStream s = new ObjectOutputStream(
new FileOutputStream(uuidFile)
);
s.writeLong(prevTime);
s.writeShort(prevClock);
s.write(prevNode);
s.close();
nextSave = prevTime + 10 * 1000 * 10; // every 10 seconds
} catch (Exception exc) {
}
}
}
}