/******************************************************************************* * Copyright (c) 2000, 2006 IBM Corporation 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.core.internal.utils; import java.io.*; import java.math.BigInteger; import java.net.InetAddress; import java.net.UnknownHostException; import java.security.SecureRandom; import java.util.GregorianCalendar; import java.util.Random; import org.eclipse.core.runtime.Assert; public class UniversalUniqueIdentifier implements java.io.Serializable { /** * All serializable objects should have a stable serialVersionUID */ private static final long serialVersionUID = 1L; /* INSTANCE FIELDS =============================================== */ private byte[] fBits = new byte[BYTES_SIZE]; /* NON-FINAL PRIVATE STATIC FIELDS =============================== */ private static BigInteger fgPreviousClockValue; private static int fgClockAdjustment = 0; private static int fgClockSequence = -1; private static byte[] nodeAddress; static { nodeAddress = computeNodeAddress(); } /* PRIVATE STATIC FINAL FIELDS =================================== */ private static Random fgRandomNumberGenerator = new Random(); /* PUBLIC STATIC FINAL FIELDS ==================================== */ public static final int BYTES_SIZE = 16; public static final byte[] UNDEFINED_UUID_BYTES = new byte[16]; public static final int MAX_CLOCK_SEQUENCE = 0x4000; public static final int MAX_CLOCK_ADJUSTMENT = 0x7FFF; public static final int TIME_FIELD_START = 0; public static final int TIME_FIELD_STOP = 6; public static final int TIME_HIGH_AND_VERSION = 7; public static final int CLOCK_SEQUENCE_HIGH_AND_RESERVED = 8; public static final int CLOCK_SEQUENCE_LOW = 9; public static final int NODE_ADDRESS_START = 10; public static final int NODE_ADDRESS_BYTE_SIZE = 6; public static final int BYTE_MASK = 0xFF; public static final int HIGH_NIBBLE_MASK = 0xF0; public static final int LOW_NIBBLE_MASK = 0x0F; public static final int SHIFT_NIBBLE = 4; public static final int ShiftByte = 8; /** UniversalUniqueIdentifier default constructor returns a new instance that has been initialized to a unique value. */ public UniversalUniqueIdentifier() { this.setVersion(1); this.setVariant(1); this.setTimeValues(); this.setNode(getNodeAddress()); } /** Constructor that accepts the bytes to use for the instance.   The format of the byte array is compatible with the <code>toBytes()</code> method. <p>The constructor returns the undefined uuid if the byte array is invalid. @see #toBytes() @see #BYTES_SIZE */ public UniversalUniqueIdentifier(byte[] byteValue) { fBits = new byte[BYTES_SIZE]; if (byteValue.length >= BYTES_SIZE) System.arraycopy(byteValue, 0, fBits, 0, BYTES_SIZE); } private void appendByteString(StringBuffer buffer, byte value) { String hexString; if (value < 0) hexString = Integer.toHexString(256 + value); else hexString = Integer.toHexString(value); if (hexString.length() == 1) buffer.append("0"); //$NON-NLS-1$ buffer.append(hexString); } private static BigInteger clockValueNow() { GregorianCalendar now = new GregorianCalendar(); BigInteger nowMillis = BigInteger.valueOf(now.getTime().getTime()); BigInteger baseMillis = BigInteger.valueOf(now.getGregorianChange().getTime()); return (nowMillis.subtract(baseMillis).multiply(BigInteger.valueOf(10000L))); } /** Simply increases the visibility of <code>Object</code>'s clone. Otherwise, no new behaviour. */ public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { Assert.isTrue(false, Messages.utils_clone); return null; } } public static int compareTime(byte[] fBits1, byte[] fBits2) { for (int i = TIME_FIELD_STOP; i >= 0; i--) if (fBits1[i] != fBits2[i]) return (0xFF & fBits1[i]) - (0xFF & fBits2[i]); return 0; } /** * Answers the node address attempting to mask the IP * address of this machine. * * @return byte[] the node address */ private static byte[] computeNodeAddress() { byte[] address = new byte[NODE_ADDRESS_BYTE_SIZE]; // Seed the secure randomizer with some oft-varying inputs int thread = Thread.currentThread().hashCode(); long time = System.currentTimeMillis(); int objectId = System.identityHashCode(new String()); ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(byteOut); byte[] ipAddress = getIPAddress(); try { if (ipAddress != null) out.write(ipAddress); out.write(thread); out.writeLong(time); out.write(objectId); out.close(); } catch (IOException exc) { //ignore the failure, we're just trying to come up with a random seed } byte[] rand = byteOut.toByteArray(); SecureRandom randomizer = new SecureRandom(rand); randomizer.nextBytes(address); // set the MSB of the first octet to 1 to distinguish from IEEE node addresses address[0] = (byte) (address[0] | (byte) 0x80); return address; } public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof UniversalUniqueIdentifier)) return false; byte[] other = ((UniversalUniqueIdentifier) obj).fBits; if (fBits == other) return true; if (fBits.length != other.length) return false; for (int i = 0; i < fBits.length; i++) { if (fBits[i] != other[i]) return false; } return true; } /** Answers the IP address of the local machine using the Java API class <code>InetAddress</code>. @return byte[] the network address in network order @see java.net.InetAddress#getLocalHost() @see java.net.InetAddress#getAddress() */ protected static byte[] getIPAddress() { try { return InetAddress.getLocalHost().getAddress(); } catch (UnknownHostException e) { //valid for this to be thrown be a machine with no IP connection //It is VERY important NOT to throw this exception return null; } } private static byte[] getNodeAddress() { return nodeAddress; } public int hashCode() { return fBits[0] + fBits[3] + fBits[7] + fBits[11] + fBits[15]; } private static int nextClockSequence() { if (fgClockSequence == -1) fgClockSequence = (int) (fgRandomNumberGenerator.nextDouble() * MAX_CLOCK_SEQUENCE); fgClockSequence = (fgClockSequence + 1) % MAX_CLOCK_SEQUENCE; return fgClockSequence; } private static BigInteger nextTimestamp() { BigInteger timestamp = clockValueNow(); int timestampComparison; timestampComparison = timestamp.compareTo(fgPreviousClockValue); if (timestampComparison == 0) { if (fgClockAdjustment == MAX_CLOCK_ADJUSTMENT) { while (timestamp.compareTo(fgPreviousClockValue) == 0) timestamp = clockValueNow(); timestamp = nextTimestamp(); } else fgClockAdjustment++; } else { fgClockAdjustment = 0; if (timestampComparison < 0) nextClockSequence(); } return timestamp; } private void setClockSequence(int clockSeq) { int clockSeqHigh = (clockSeq >>> ShiftByte) & LOW_NIBBLE_MASK; int reserved = fBits[CLOCK_SEQUENCE_HIGH_AND_RESERVED] & HIGH_NIBBLE_MASK; fBits[CLOCK_SEQUENCE_HIGH_AND_RESERVED] = (byte) (reserved | clockSeqHigh); fBits[CLOCK_SEQUENCE_LOW] = (byte) (clockSeq & BYTE_MASK); } protected void setNode(byte[] bytes) { for (int index = 0; index < NODE_ADDRESS_BYTE_SIZE; index++) fBits[index + NODE_ADDRESS_START] = bytes[index]; } private void setTimestamp(BigInteger timestamp) { BigInteger value = timestamp; BigInteger bigByte = BigInteger.valueOf(256L); BigInteger[] results; int version; int timeHigh; for (int index = TIME_FIELD_START; index < TIME_FIELD_STOP; index++) { results = value.divideAndRemainder(bigByte); value = results[0]; fBits[index] = (byte) results[1].intValue(); } version = fBits[TIME_HIGH_AND_VERSION] & HIGH_NIBBLE_MASK; timeHigh = value.intValue() & LOW_NIBBLE_MASK; fBits[TIME_HIGH_AND_VERSION] = (byte) (timeHigh | version); } protected synchronized void setTimeValues() { this.setTimestamp(timestamp()); this.setClockSequence(fgClockSequence); } protected int setVariant(int variantIdentifier) { int clockSeqHigh = fBits[CLOCK_SEQUENCE_HIGH_AND_RESERVED] & LOW_NIBBLE_MASK; int variant = variantIdentifier & LOW_NIBBLE_MASK; fBits[CLOCK_SEQUENCE_HIGH_AND_RESERVED] = (byte) ((variant << SHIFT_NIBBLE) | clockSeqHigh); return (variant); } protected void setVersion(int versionIdentifier) { int timeHigh = fBits[TIME_HIGH_AND_VERSION] & LOW_NIBBLE_MASK; int version = versionIdentifier & LOW_NIBBLE_MASK; fBits[TIME_HIGH_AND_VERSION] = (byte) (timeHigh | (version << SHIFT_NIBBLE)); } private static BigInteger timestamp() { BigInteger timestamp; if (fgPreviousClockValue == null) { fgClockAdjustment = 0; nextClockSequence(); timestamp = clockValueNow(); } else timestamp = nextTimestamp(); fgPreviousClockValue = timestamp; return fgClockAdjustment == 0 ? timestamp : timestamp.add(BigInteger.valueOf(fgClockAdjustment)); } /** This representation is compatible with the (byte[]) constructor. @see #UniversalUniqueIdentifier(byte[]) */ public byte[] toBytes() { byte[] result = new byte[fBits.length]; System.arraycopy(fBits, 0, result, 0, fBits.length); return result; } public String toString() { StringBuffer buffer = new StringBuffer(); for (int i = 0; i < fBits.length; i++) appendByteString(buffer, fBits[i]); return buffer.toString(); } public String toStringAsBytes() { String result = "{"; //$NON-NLS-1$ for (int i = 0; i < fBits.length; i++) { result += fBits[i]; if (i < fBits.length + 1) result += ","; //$NON-NLS-1$ } return result + "}"; //$NON-NLS-1$ } }