Mega Code Archive

 
Categories / Java / Development Class
 

Create GUIDs according to the principles at http

/*************************************************************************  *  * ADOBE CONFIDENTIAL  * __________________  *  *  Copyright 2002 - 2007 Adobe Systems Incorporated  *  All Rights Reserved.  *  * NOTICE:  All information contained herein is, and remains  * the property of Adobe Systems Incorporated and its suppliers,  * if any.  The intellectual and technical concepts contained  * herein are proprietary to Adobe Systems Incorporated  * and its suppliers and may be covered by U.S. and Foreign Patents,  * patents in process, and are protected by trade secret or copyright law.  * Dissemination of this information or reproduction of this material  * is strictly forbidden unless prior written permission is obtained  * from Adobe Systems Incorporated.  **************************************************************************/ //package flex.messaging.util; import java.security.SecureRandom; import java.util.Random; /**  * Static class that creates GUIDs according to the principles laid out in  * at http://www.opengroup.org/dce/info/draft-leach-uuids-guids-01.txt.  * Specifically, these GUIDs have elements specific to the time and place of  * their creation; a unique TOD value (but the low-level IEEE 802/MAC address  * of the host system is not determined). The rest of the GUID is a sequence of  * random hex digits from a cryptographically strong random number generator.  *  *<p> This class differs from the Leach specification in a few ways. It does not use a  * persistent store to track the clock sequence and does not coordinate ids  * given out by 2 processes on the same machine.  It does not follow the formal  * clock sequence guidelines but instead uses random numbers for all but the  * timestamp.</p>  */ public class UUIDUtils {     /**  Cryptographically strong random number generator. */     private static SecureRandom _rand = new SecureRandom();     private static Random _weakRand = new Random();     /**      * The spec indicates that our time value should be based on 100 nano      * second increments but our time granularity is in milliseconds.      * The spec also says we can approximate the time by doing an increment      * when we dole out new ids in the same millisecond.  We can fit 10,000      * 100 nanos into a single millisecond.      */     private static final int MAX_IDS_PER_MILLI = 10000;     /**      *  Any given time-of-day value can only be used once; remember the last used      *  value so we don't reuse them.      *  <p>NOTE: this algorithm assumes the clock will not be turned back.</p>      */     private static long lastUsedTOD = 0;     /** Counter to use when we need more than one id in the same millisecond. */     private static int numIdsThisMilli = 0;     /**  Hex digits, used for padding UUID strings with random characters. */     private static final String alphaNum = "0123456789ABCDEF";     /** 4 bits per hex character. */     private static final int BITS_PER_DIGIT = 4;     private static final int BITS_PER_INT = 32;     private static final int BITS_PER_LONG = 64;     private static final int DIGITS_PER_INT = BITS_PER_INT / BITS_PER_DIGIT;     private static final int DIGITS_PER_LONG = BITS_PER_LONG / BITS_PER_DIGIT;     private static char[] UPPER_DIGITS = new char[] {         '0', '1', '2', '3', '4', '5', '6', '7',         '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',     };     /**      * Private constructor to prevent instances from being created.      */     private UUIDUtils()     {     }     /**      *      * Use the createUUID function when you need a unique string that you will      * use as a persistent identifier in a distributed environment. To a very      * high degree of certainty, this function returns a unique value; no other      * invocation on the same or any other system should return the same value.      *      * @return a Universally Unique Identifier (UUID)      * Proper Format: `XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'      * where `X' stands for a hexadecimal digit (0-9 or A-F).      * @author <a href="mailto:mnimer@macromedia.com">Mike Nimer</a>      * @author <a href="mailto:pfriedman@macromedia.com">Paul Friedman</a>      */     public static String createUUID()     {         return createUUID(true);     }     /**      * @param secure Boolean indicating whether to create a secure UUID.      * @see #createUUID()      */     public static String createUUID(boolean secure)     {         Random rand = secure ? _rand : _weakRand;         StringBuffer s = new StringBuffer(36);         appendHexString(uniqueTOD(), false, 11, s);         //  Just use random padding characters, but ensure that the high bit         //  is set to eliminate chances of collision with an IEEE 802 address.         s.append(  alphaNum.charAt( rand.nextInt(16) | 8 ) );         //  Add random padding characters.         appendRandomHexChars(32 - s.length(), rand, s);         //insert dashes in proper position. so the format matches CF         s.insert(8,"-");         s.insert(13,"-");         s.insert(18,"-");         s.insert(23,"-");         return s.toString();     }     /**      * Converts a 128-bit UID encoded as a byte[] to a String representation.      * The format matches that generated by createUID. If a suitable byte[]      * is not provided, null is returned.      *      * @param ba byte[] 16 bytes in length representing a 128-bit UID.      *      * @return String representation of the UID, or null if an invalid      * byte[] is provided.      */     public static String fromByteArray(byte[] ba)     {         if (ba != null && ba.length == 16)         {             StringBuffer result = new StringBuffer(36);             for (int i = 0; i < 16; i++)             {                 if (i == 4 || i == 6 || i == 8 || i == 10)                     result.append('-');                 result.append(UPPER_DIGITS[(ba[i] & 0xF0) >>> 4]);                 result.append(UPPER_DIGITS[(ba[i] & 0x0F)]);             }             return result.toString();         }         return null;     }     /**      * A utility method to check whether a String value represents a      * correctly formatted UID value. UID values are expected to be      * in the format generated by createUID(), implying that only      * capitalized A-F characters in addition to 0-9 digits are      * supported.      *      * @param uid The value to test whether it is formatted as a UID.      *      * @return Returns true if the value is formatted as a UID.      */     public static boolean isUID(String uid)     {         if (uid != null && uid.length() == 36)         {             char[] chars = uid.toCharArray();             for (int i = 0; i < 36; i++)             {                 char c = chars[i];                 // Check for correctly placed hyphens                 if (i == 8 || i == 13 || i == 18 || i == 23)                 {                     if (c != '-')                     {                         return false;                     }                 }                 // We allow capital alpha-numeric hex digits only                 else if (c < 48 || c > 70 || (c > 57 && c < 65))                 {                     return false;                 }             }             return true;         }         return false;     }     /**      * Converts a UID formatted String to a byte[]. The UID must be in the      * format generated by createUID, otherwise null is returned.      *      * @param uid String representing a 128-bit UID.      *      * @return byte[] 16 bytes in length representing the 128-bits of the      * UID or null if the uid could not be converted.      */     public static byte[] toByteArray(String uid)     {         if (isUID(uid))         {             byte[] result = new byte[16];             char[] chars = uid.toCharArray();             int r = 0;             for (int i = 0; i < chars.length; i++)             {                 if (chars[i] == '-')                     continue;                 int h1 = Character.digit(chars[i], 16);                 i++;                 int h2 = Character.digit(chars[i], 16);                 result[r++] = (byte)(((h1 << 4) | h2) & 0xFF);             }             return result;         }         return null;     }     private static void appendRandomHexChars(int n, Random rand, StringBuffer result)     {         int digitsPerInt = DIGITS_PER_INT;         while (n > 0)         {             int digitsToUse = Math.min(n, digitsPerInt);             n -= digitsToUse;             appendHexString(rand.nextInt(), true, digitsToUse, result);         }     }     private static void appendHexString         (long value, boolean prependZeroes, int nLeastSignificantDigits,          StringBuffer result)     {         int bitsPerDigit = BITS_PER_DIGIT;         long mask = (1L << bitsPerDigit) - 1;         if (nLeastSignificantDigits < DIGITS_PER_LONG)         {             // Clear the bits that we don't care about.             value &= (1L << (bitsPerDigit * nLeastSignificantDigits)) - 1;         }         // Reorder the sequence so that the first set of bits will become the         // last set of bits.         int i = 0;         long reorderedValue = 0;         if (value == 0)         {             // One zero is dumped.             i++;         }         else         {             do             {                 reorderedValue = (reorderedValue << bitsPerDigit) | (value & mask);                 value >>>= bitsPerDigit;                 i++;             } while (value != 0);         }         if (prependZeroes)         {             for (int j = nLeastSignificantDigits - i; j > 0; j--)             {                 result.append('0');             }         }         // Dump the reordered sequence, with the most significant character         // first.         for (; i > 0; i--)         {             result.append(alphaNum.charAt((int) (reorderedValue & mask)));             reorderedValue >>>= bitsPerDigit;         }     }     /**      *  @return a time value, unique for calls to this method loaded by the same classloader.      */     private static synchronized long uniqueTOD()     {         long currentTOD = System.currentTimeMillis();         // Clock was set back... do not hang in this case waiting to catch up.         // Instead, rely on the random number part to differentiate the ids.         if (currentTOD < lastUsedTOD)             lastUsedTOD = currentTOD;         if (currentTOD == lastUsedTOD)         {             numIdsThisMilli++;             /*              * Fall back to the old technique of sleeping if we allocate              * too many ids in one time interval.              */             if (numIdsThisMilli >= MAX_IDS_PER_MILLI)             {                 while ( currentTOD == lastUsedTOD )                 {                     try { Thread.sleep(1); } catch ( Exception interrupt ) { /* swallow, wake up */ }                     currentTOD = System.currentTimeMillis();                 }                 lastUsedTOD = currentTOD;                 numIdsThisMilli = 0;             }         }         else         {             //  We have a new TOD, reset the counter             lastUsedTOD = currentTOD;             numIdsThisMilli = 0;         }         return lastUsedTOD * MAX_IDS_PER_MILLI + (long)numIdsThisMilli;     } }