All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.day.util.UUID Maven / Gradle / Ivy

There is a newer version: 2024.9.17689.20240905T073330Z-240800
Show newest version
/*************************************************************************
 * 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) { } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy