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

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

/**
 * $Id: UUID.java 13996 2005-04-12 14:40:10Z tripod $
 *
 * Copyright 1997-2004 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 *
 */

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"
     */
    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.
     */
    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
     */
    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.
     * 
     */
    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.
     */
    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
     */
    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]
     */
    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