1
2
3
4
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 }