View Javadoc

1   /*
2    * GUIDGenerator.java
3    *
4    * Created on 29 August 2001, 09:41
5    */
6   
7   package org.portletbridge.portlet;
8   
9   import java.net.InetAddress;
10  import java.rmi.RemoteException;
11  import java.security.SecureRandom;
12  
13  /***
14   * <p>
15   * This GUID generator  can be safely pooled on a single machine and deployed in a cluster to
16   * provide completely scalable performance.
17   * </p><p>
18   * The problem of generating unique IDs can essentially be broken down
19   * as uniqueness over space and uniqueness over time which, when combined,
20   * produces a globally unique sequence.
21   * </p><p>
22   * Taking the UUID and GUID Internet standards draft for the Network Working
23   * Group by Paul J. Leach, Microsoft, and Rich Salz, Certco, as a starting
24   * point we assume that the GUID be represented as a 36-digit alphanumeric
25   * (including hyphens) of the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
26   * </p><p>
27   * The first 20 characters represent the uniqueness over time
28   * (the low 32-bits of the time stamp as the first 8, the next 4 as the
29   * mid 16 bits of the time stamp, the next 4 as the high value of the time
30   * stamp multiplexed with the version, and the next 4 a combination of the
31   * lock sequence high -multiplexed with the variant field - and the low bits)
32   * </p><p>
33   * Note: The internet guidelines suggest the timestamp as a 60-bit value to
34   * a precision of 100ns since 00:00:00.00, 15 October 1582
35   * (the date of Gregorian reform to the Christian calendar)
36   * </p><p>
37   * The last 12 characters are a 48-bit node identifier usually implemented
38   * as the IEEE 802 address, which gives uniqueness over space.
39   * </p><p>
40   * These are combined to produce a unique sequence.
41   * <br>
42   * Some of the main problems in an EJB implementation of this technique are: -
43   * <ul>
44   * <li>1) there is no access to the traditional IEEE 802 address.</li>
45   * <li>2) more than one object can exist at the same time and so
46   * generate the same time stamp at a granularity of a millisecond
47   * (Java's timestamp granularity).</li>
48   * <li> 3) a clock sequence or equivalent is used as a way of reading/writing
49   * a value to storage (e.g. a database or system file) that can be used in case
50   * the clock is set backwards and as a seed for the number sequence.
51   * This is even more of a problem when a cluster of machines do not use the
52   * same database.</li>
53   * <li>4) Singletons are not portable and not recommended in the EJB specs.</li>
54   * </ul>
55   *
56   *
57   * <p>
58   * The GUID is constucted by.
59   * <ul>
60   * <li>
61   * 1) (1-8 hex characters) use the low 32 bits of System.currentTimeMillis().
62   * Note: could use the recommended date format by adding 12219292800000L to the
63   * system time long before grabbing the low 32 bits. <br>
64   * This gives us a uniqueness of a millisecond -
65   * therefore any clashing object will have to be generated within the same
66   * millisecond.
67   * </li>
68   * <li>
69   * 2) (9-16 hex characters) the IP address of the machine as a hex
70   * representation of the 32 bit integer underlying IP -
71   * gives us a spatial uniqueness to a single machine -
72   * guarantees that these characters will be different for machines in a
73   * cluster or on a LAN. Note: This is not appropriate for a global addressing scheme
74   * to distinguish java objects in any JVM on the Internet.
75   * </li>
76   * <li>
77   * 3) (17-24 hex characters) the hex value of the Stateless Session bean
78   * object's hashCode (a 32 bit int) - in the Java language spec -
79   * the hashcode value of Object is defined as -
80   * <I>As much as is reasonably practical, the hashCode method defined by class
81   * object does return distinct integers for distinct objects.
82   * (This is typically implemented by converting the internal address of the
83   * object into an integer, but this implementation technique is not required by
84   * the Java programming language.)</I>**
85   * </li>
86   *
87   * <li>
88   * 4) (25-32 hex characters) a random 32 bit integer generated for each method
89   * invocation from the SecureRandom java class using SecureRandom.nextInt().
90   * This method produces a cryptographically strong pseudo-random integer.
91   * The Java lang defines this as -
92   * <I>Returns a pseudo-random, uniformly distributed int value drawn from this
93   * random number generator's sequence. The general contract of nextInt is that
94   * one int value in the specified range is pseudorandomly generated and
95   * returned. All n possible int values are produced with (approximately)
96   * equal probability.</I>**
97   * </li>
98   * </ul>
99   * <p>
100  * This gives us a value that is a combination of
101  * <ul>
102  * <li>
103  * 1) is unique to the millisecond</li>
104  * <li>2) is unique to the machine</li>
105  * <li>3) is unique to the object creating it</li>
106  * <li>4) is unique to the method call for the same object</li>
107  * </ul>
108  * <p>
109  * Note: the potential theoretical conflicts are:
110  * <ul>
111  * <li>1) that two objects on the same machine are assigned the exact same hashCode
112  * (I do not know of any implementations that do this but there may be some out
113  * there) and at the same millisecond must also get the same integer value from
114  * the SecureRandom implementation.</li>
115  * <li>2) The same int value is returned from the SecureRandom object in subsequent
116  * method calls for the same object in the same millisecond.</li>
117  * <li>3) A reset clock (which would require a redeployment of the bean) will
118  * produce an identical hashcode for the new deployment as a previous one
119  * AND the random values will have to be the same in the same repeated
120  * millisecond value as in a previous sequence.
121  * </li>
122  * </ul>
123  * @author Steve Woodcock
124  * @version 1.1
125  */
126 public class GUIDGenerator {
127 
128     /*** Creates new GUIDGenerator */
129     public GUIDGenerator()
130         throws GUIDException 
131     {
132         
133          try
134         {
135             seeder = new SecureRandom();
136             InetAddress inetaddress = InetAddress.getLocalHost();
137             byte abyte0[] = inetaddress.getAddress();
138             String s = urlFriendlyFormat(getInt(abyte0), 8);
139             midValue = s;
140             midValueUnformated = s;
141         }
142         catch(Exception exception)
143         {
144             throw new GUIDException("error - failure to instantiate GUIDGenerator" +
145                 exception);
146         }
147     }
148 
149     
150       /*** <p>
151      * The private method that actually does the work. The String passed into the
152      * method is either the formatted or unformatted mid value which is combined
153      * with the low 32 bits (obtained by a bit wise &) of the time and the next value
154      * in the secureRandom sequence.
155      * </p>
156      * @param s The string containing the mid value of the required format for the UUID.
157      * @return A string containing the UUID in the desired format.
158 
159      */    
160     private String getVal(String s)
161     {        
162             int i = (int)System.currentTimeMillis() & 0xffffffff;
163             int j = seeder.nextInt();
164             return urlFriendlyFormat(i, 8) + s + urlFriendlyFormat(j, 8);
165     }
166       /*** <p>
167      * Used to provide a UUID that does not conform to the GUID RFC. The String
168      * returned does not have the xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
169      * instead it is xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx. This is to provide a shorter
170      * version of the UUID for easier database manipulation.
171      * </p>
172      * <p>
173      * However, it is recommended that th full format be used.
174      * </P>
175      * @throws RemoteException Required to be thrown by the EJB specification.
176      * @return A String representing a UUID in the format xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
177      * Each character in the string is a hexadecimal.
178      */    
179     public String getUnformatedUUID()
180     {
181         return getVal(midValueUnformated);
182     }
183 
184     /*** <p>
185      * Returns a UUID formated according to the draft internet standard. See
186      * the class level documentation for more details.
187      * </P>
188      *
189      * @return A String representing a UUID in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
190      */    
191     public String getUUID()
192     {
193         return getVal(midValue);
194     }
195 
196     /*** <p>
197      * A utility method to take a byte array of 4 bytes and produce an int value. This
198      * is used to convert the quad xxx.xxx.xxx.xxx value of the IP address to the
199      * underlying 32-bit int that the ip address represents. There is no way to
200      * obtain this value in Java so we need to convert it ourselves.
201      * </P
202      * @param abyte0 Th byte array containg 4 bytes that represent an IP address.
203      * @return An int that is the actual value of the ip address.
204      */    
205     private int getInt(byte abyte0[])
206     {
207         int i = 0;
208         int j = 24;
209         for(int k = 0; j >= 0; k++)
210         {
211             int l = abyte0[k] & 0xff;
212             i += l << j;
213             j -= 8;
214         }
215 
216         return i;
217     }
218 
219     /*** <p>
220      * A utility method to produce a correctly formatted hex string string from an int
221      * value and and an int specifying the length the hex string that represents the
222      * int value should be.
223      * </p>
224      * <p>
225      * Utilises both the padHex and toHexString methods.
226      * </p>
227      * @param i The int value that is to be transformed to a hex string.
228      * @param j An int specifying the length of the hex string to be returned.
229      * @return A string that contains the formatted hex string.
230      */    
231     private String urlFriendlyFormat(int i, int j)
232     {
233         StringBuffer result = new StringBuffer();
234         do {
235             result.append(table[(i & 63)]);
236             i >>>= 6;
237         } while(i > 0);
238         return result.toString();
239     }
240     
241     private static final char[] table = {
242             '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 
243             'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 
244             'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 
245             'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '*', '-' 
246     };
247     
248     /***
249      * <p>The random seed used in the method call to provide the required randomized
250      * element. The normal random class is not used as the sequences produced are more
251      * uniform than this implementation and will produce a predictable sequence which
252      * could lead to a greater chance of number clashes.
253      * <p>
254      */    
255     private SecureRandom seeder;
256     /*** <p>
257      * The cached mid value of the UUID. This consists of the hexadecimal version of
258      * the IP address of the machine and the object's hashcode. These are stored as
259      * -xxxx-xxxx-xxxx-xxxx to speed up the method calls. This value does not change
260      * over the lifespan of the object and so is able to be cached in this manner.
261      * <p>
262      */    
263     private String midValue;
264     /*** <p>
265      * The unformatted cached mid value of the UUID. This consists of the hexadecimal
266      * version of the IP address of the machine and the object's hashcode. These are
267      * stored as xxxxxxxxxxxxxxxx to speed up the method calls. This value does not change
268      * over the lifespan of the object and so is able to be cached in this manner. This
269      * vlaue is used to supply the middle part of the UUID for the unformatted method.
270      * <p>
271      */    
272     private String midValueUnformated;
273 }