001: package com.ibm.webdav.impl;
002:
003: /*
004: * (C) Copyright IBM Corp. 2000 All rights reserved.
005: *
006: * The program is provided "AS IS" without any warranty express or
007: * implied, including the warranty of non-infringement and the implied
008: * warranties of merchantibility and fitness for a particular purpose.
009: * IBM will not be liable for any damages suffered by you as a result
010: * of using the Program. In no event will IBM be liable for any
011: * special, indirect or consequential damages or lost profits even if
012: * IBM has been advised of the possibility of their occurrence. IBM
013: * will not be liable for any third party claims against you.
014: *
015: * Portions Copyright (C) Simulacra Media Ltd, 2004.
016: */
017:
018: import java.net.InetAddress;
019: import java.util.Date;
020: import java.util.Random;
021: import java.io.*;
022: import java.security.MessageDigest;
023:
024: /** A Universally Unique Identifier (UUID) is a 128 bit number generated
025: * according to an algorithm that is garanteed to be unique in time and
026: * space from all other UUIDs. It consists of an IEEE 802 Internet Address
027: * and various time stamps to ensure uniqueness. For a complete specification,
028: * see ftp://ietf.org/internet-drafts/draft-leach-uuids-guids-01.txt [leach].
029: * @author Jim Amsden <jamsden@us.ibm.com>
030: */
031: public class UUID implements Serializable {
032: private static byte[] internetAddress = null;
033: private static String uuidFile = null;
034: private long time;
035: private short clockSequence;
036: private byte version = 1;
037: private byte[] node = new byte[6];
038: private static final int UUIDsPerTick = 128;
039: private static long lastTime = new Date().getTime();
040: private static int uuidsThisTick = UUIDsPerTick;
041: private static UUID previousUUID = null; // initialized from saved state
042: private static long nextSave = new Date().getTime();
043: private static Random randomGenerator = new Random(new Date()
044: .getTime());
045: private static char[] hexDigits = { '0', '1', '2', '3', '4', '5',
046: '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
047:
048: //---------------------------------------------------------------------
049:
050: static {
051: try {
052: internetAddress = InetAddress.getLocalHost().getAddress();
053: } catch (Exception exc) {
054: System.err.println("Can't get host address: " + exc);
055: exc.printStackTrace();
056: }
057: try {
058: uuidFile = System.getProperty("UUID_HOME");
059: if (uuidFile == null) {
060: uuidFile = System.getProperty("java.home");
061: }
062: if (!uuidFile.endsWith(File.separator)) {
063: uuidFile = uuidFile + File.separator;
064: }
065: uuidFile = uuidFile + "UUID";
066: previousUUID = getUUIDState();
067: } catch (Exception exc) {
068: exc.printStackTrace();
069: }
070: }
071:
072: /** Generate a UUID for this host using version 1 of [leach].
073: */
074: public UUID() {
075: synchronized (this ) {
076: time = getCurrentTime();
077: //node = getIEEEAddress();
078: node = previousUUID.getNode();
079: if (previousUUID == null || nodeChanged(previousUUID)) {
080: // if there is no saved state, or the node address changed,
081: // generate a random clock sequence
082: clockSequence = (short) random();
083: } else if (time < previousUUID.getTime()) {
084: // if the clock was turned back, increment the clock sequence
085: clockSequence++;
086: }
087: previousUUID = this ; // for next time
088:
089: // save for the next UUID
090: setUUIDState(this );
091: }
092: }
093:
094: /** Generate a UUID for this host using version 1 of [leach].
095: * @param node the node to use in the UUID
096: */
097: public UUID(byte[] node) {
098: synchronized (this ) {
099: time = getCurrentTime();
100: this .node = node;
101:
102: // save for the next UUID
103: setUUIDState(this );
104: }
105: }
106:
107: /** Generate a UUID from a name (NOT IMPLEMENTED)
108: */
109: public UUID(UUID context, String name) {
110: }
111:
112: /** Lexically compare this UUID with withUUID. Note: lexical ordering
113: * is not temporal ordering.
114: *
115: * @param withUUID the UUID to compare with
116: * @return
117: * <ul>
118: * <li>-1 if this UUID is less than withUUID
119: * <li>0 if this UUID is equal to withUUID
120: * <li>1 if this UUID is greater than withUUID
121: * </ul>
122: */
123: public int compare(UUID withUUID) {
124: if (time < withUUID.getTime()) {
125: return -1;
126: } else if (time > withUUID.getTime()) {
127: return 1;
128: }
129: if (version < withUUID.getVersion()) {
130: return -1;
131: } else if (version > withUUID.getVersion()) {
132: return 1;
133: }
134: if (clockSequence < withUUID.getClockSequence()) {
135: return -1;
136: } else if (clockSequence > withUUID.getClockSequence()) {
137: return 1;
138: }
139: byte[] withNode = withUUID.getNode();
140: for (int i = 0; i < 6; i++) {
141: if (node[i] < withNode[i]) {
142: return -1;
143: } else if (node[i] > withNode[i]) {
144: return 1;
145: }
146: }
147: return 0;
148: }
149:
150: /** Get a 48 bit cryptographic quality random number to use as the node field
151: * of a UUID as specified in section 6.4.1 of version 10 of the WebDAV spec.
152: * This is an alternative to the IEEE 802 host address which is not available
153: * from Java. The number will not conflict with any IEEE 802 host address because
154: * the most significant bit of the first octet is set to 1.
155: * @return a 48 bit number specifying an id for this node
156: */
157: private static byte[] computeNodeAddress() {
158: byte[] address = new byte[6];
159: // create a random number by concatenating:
160: // the hash code for the current thread
161: // the current time in milli-seconds
162: // the internet address for this node
163: int thread = Thread.currentThread().hashCode();
164: long time = System.currentTimeMillis();
165: ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
166: DataOutputStream out = new DataOutputStream(byteOut);
167: try {
168: if (internetAddress != null) {
169: out.write(internetAddress);
170: }
171: out.write(thread);
172: out.writeLong(time);
173: out.close();
174: } catch (IOException exc) {
175: }
176: byte[] rand = byteOut.toByteArray();
177: MessageDigest md5 = null;
178: try {
179: md5 = MessageDigest.getInstance("MD5");
180: } catch (Exception exc) {
181: }
182: md5.update(rand);
183: byte[] temp = md5.digest();
184: // pick the middle 6 bytes of the MD5 digest
185: for (int i = 0; i < 6; i++) {
186: address[i] = temp[i + 5];
187: }
188: // set the MSB of the first octet to 1 to distinguish from IEEE node addresses
189: address[0] = (byte) (address[0] | (byte) 0x80);
190: return address;
191: }
192:
193: /** Get the clock sequence number.
194: * @return the clock sequence number
195: */
196: public int getClockSequence() {
197: return clockSequence;
198: }
199:
200: /** Get the current time compensating for the fact that the real
201: * clock resolution may be less than 100ns.
202: *
203: * @return the current date and time
204: */
205: private static long getCurrentTime() {
206: long now = 0;
207: boolean waitForTick = true;
208: while (waitForTick) {
209: now = new Date().getTime();
210: if (lastTime < now) {
211: // got a new tick, make sure uuidsPerTick doesn't cause an overrun
212: uuidsThisTick = 0;
213: waitForTick = false;
214: } else if (uuidsThisTick < UUIDsPerTick) {
215: //
216: uuidsThisTick++;
217: waitForTick = false;
218: }
219: }
220: // add the uuidsThisTick to the time to increase the clock resolution
221: now += uuidsThisTick;
222: lastTime = now;
223: return now;
224: }
225:
226: /** Get the 48 bit IEEE 802 host address. NOT IMPLEMENTED
227: * @return a 48 bit number specifying a unique location
228: */
229: private static byte[] getIEEEAddress() {
230: byte[] address = new byte[6];
231: // TODO: get the IEEE 802 host address
232: return address;
233: }
234:
235: /** Get the spatially unique portion of the UUID. This is either
236: * the 48 bit IEEE 802 host address, or if on is not available, a random
237: * number that will not conflict with any IEEE 802 host address.
238: * @return node portion of the UUID
239: */
240: public byte[] getNode() {
241: return node;
242: }
243:
244: /** Get the temporal unique portion of the UUID.
245: * @return the time portion of the UUID
246: */
247: public long getTime() {
248: return time;
249: }
250:
251: /** Get the 128 bit UUID.
252: */
253: public byte[] getUUID() {
254: byte[] uuid = new byte[16];
255: long t = time;
256: for (int i = 0; i < 8; i++) {
257: uuid[i] = (byte) ((t >> 8 * i) & 0xFF); // time
258: }
259: uuid[7] |= (byte) (version << 12); // time hi and version
260: uuid[8] = (byte) (clockSequence & 0xFF);
261: uuid[9] = (byte) ((clockSequence & 0x3F00) >> 8);
262: uuid[9] |= 0x80; // clock sequence hi and reserved
263: for (int i = 0; i < 6; i++) {
264: uuid[10 + i] = node[i]; // node
265: }
266: return uuid;
267: }
268:
269: /** Get the UUID generator state. This consists of the last (or
270: * nearly last) UUID generated. This state is used in the construction
271: * of the next UUID. May return null if the UUID state is not
272: * available.
273: * @return the last UUID generator state
274: */
275: private static UUID getUUIDState() {
276: UUID uuid = null;
277: try {
278: FileInputStream in = new FileInputStream(uuidFile);
279: ObjectInputStream s = new ObjectInputStream(in);
280: uuid = (UUID) s.readObject();
281: } catch (Exception exc) {
282: uuid = new UUID(computeNodeAddress());
283: System.err.println("Can't get saved UUID state: " + exc);
284: }
285: return uuid;
286: }
287:
288: /** Get the UUID version number.
289: */
290: public int getVersion() {
291: return version;
292: }
293:
294: /** Compare two UUIDs
295: * @return true if the UUIDs are equal
296: */
297: public boolean isEqual(UUID toUUID) {
298: return compare(toUUID) == 0;
299: }
300:
301: /** Determine if the node changed with respect to previousUUID.
302: * @param previousUUID the UUID to compare with
303: * @return true if the the previousUUID has a different node than this UUID
304: */
305: private boolean nodeChanged(UUID previousUUID) {
306: byte[] previousNode = previousUUID.getNode();
307: boolean nodeChanged = false;
308: int i = 0;
309: while (!nodeChanged && i < 6) {
310: nodeChanged = node[i] != previousNode[i];
311: i++;
312: }
313: return nodeChanged;
314: }
315:
316: /** Generate a crypto-quality random number. This implementation
317: * doesn't do that.
318: * @return a random number
319: */
320: private static int random() {
321: return randomGenerator.nextInt();
322: }
323:
324: /** Set the persistent UUID state.
325: * @param aUUID the UUID to save
326: */
327: private static void setUUIDState(UUID aUUID) {
328: if (aUUID.getTime() > nextSave) {
329: try {
330: FileOutputStream f = new FileOutputStream(uuidFile);
331: ObjectOutputStream s = new ObjectOutputStream(f);
332: s.writeObject(aUUID);
333: s.close();
334: nextSave = aUUID.getTime() + 10 * 10 * 1000 * 1000;
335: } catch (Exception exc) {
336: System.err.println("Can't save UUID state: " + exc);
337: }
338: }
339: }
340:
341: /** Provide a String representation of a UUID as specified in section
342: * 3.5 of [leach].
343: */
344: public String toString() {
345: byte[] uuid = getUUID();
346: StringWriter s = new StringWriter();
347: for (int i = 0; i < 16; i++) {
348: s.write(hexDigits[(uuid[i] & 0xF0) >> 4]);
349: s.write(hexDigits[uuid[i] & 0x0F]);
350: if (i == 3 || i == 5 || i == 7 || i == 9) {
351: s.write('-');
352: }
353: }
354: return s.toString();
355: }
356: }
|