/*
* $Date: 2006-06-24 21:02:12 +1000 (Sat, 24 Jun 2006) $
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2003, 2004 The JRDF Project. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* the JRDF Project (http://jrdf.sf.net/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The JRDF Project" and "JRDF" must not be used to endorse
* or promote products derived from this software without prior written
* permission. For written permission, please contact
* newmana@users.sourceforge.net.
*
* 5. Products derived from this software may not be called "JRDF"
* nor may "JRDF" appear in their names without prior written
* permission of the JRDF Project.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the JRDF Project. For more
* information on JRDF, please see <http://jrdf.sourceforge.net/>.
*/
package org.mulgara.util;
//Java 2 standard packages
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.rmi.dgc.VMID;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
/**
* Utility class that generates an Unique identifier.
*
* @author <a href="mailto:robert.turner@tucanatech.com">Robert Turner</a>
*/
public class UIDGenerator {
/** Computers IP address. */
private static String ipAddress;
/** Unique Java Virtual Machine Identifier. */
private static String vmID;
/** time the method was called (used to prevent duplicates). */
private static long callTime;
/** count of UIDs generated by this JVM (to avoid per millisecond duplicates). */
private static long uidCounter;
private static final int SINGLE_DIGIT = 0x10;
private static final int INT_OFFSET = 0xFF;
private UIDGenerator() {
}
/**
* Generates an Unique Identifier using the current time and the machines
* IP address.
*
* @throws Exception
* @return String
*/
public static synchronized String generateUID() throws Exception {
String uniqueID = getUniqueID(getSeed());
return uniqueID;
}
/**
* Returns a MD5 sum of the seed.
*
* @param seed char[]
* @throws Exception
* @return String
*/
private static synchronized String getUniqueID(char[] seed) throws
Exception {
String uid = null;
//digest the seed and convert to hex
byte[] digested = digest(seed);
StringBuffer buffer = new StringBuffer();
int currentInt = 0;
//convert each byte to an int (as hex)
for (int i = 0; i < digested.length; ++i) {
//conver to int
currentInt = digested[i] & INT_OFFSET;
//is the int smaller than 16? (single digit hex)
if (SINGLE_DIGIT > currentInt) {
buffer.append('0');
}
buffer.append(Integer.toHexString(currentInt));
}
uid = buffer.toString();
//validate
if (null == uid) {
throw new Exception("Failed to generate UID.");
}
return uid;
}
/**
* Returns an MD5 sum for the char [] .
*
* @param chars []
* @throws Exception
* @return byte[]
*/
private static byte[] digest(char[] chars) throws Exception {
//validate
if (null == chars) {
throw new IllegalArgumentException("Cannot get MD5 sum for null char [].");
}
try {
MessageDigest digest = MessageDigest.getInstance("MD5");
//add the chars to the buffer
int bufferSize = chars.length * 2;
ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
for (int i = 0; i < chars.length; i++) {
buffer.putChar(chars[i]);
}
//digest
return digest.digest(buffer.array());
}
catch (NoSuchAlgorithmException algorithmException) {
throw new Exception("Could not get MD5 algorithm.", algorithmException);
}
}
/**
* Returns an unique seed.
*
* @throws Exception
* @return char []
*/
private static synchronized char[] getSeed() throws Exception {
StringBuffer seed = new StringBuffer();
//location in universe (cyberspace). A JVM running on a particular machine.
seed.append(getIP());
seed.append(getJVMID());
//time (current millisecond)
seed.append(getTime());
//random, allows multiple new UIDS per millisecond. (validated by List)
seed.append(getRandom());
//another Class could be generating UIDS the same way within the same JVM
seed.append(UIDGenerator.class.getName());
//two random numbers could (possibly) be generated in the same millisecond
seed.append(getCount());
//more than one UIDGenerator class may be loaded by different ClassLoaders
seed.append(getClassLoaderId());
//conver to char []
char[] chars = new char[seed.length()];
seed.getChars(0, seed.length(), chars, 0);
return chars;
}
/**
* Returns a Random number/String.
*
* @return String
*/
private static synchronized String getRandom() {
long random = new SecureRandom().nextLong();
return "" + random;
}
/**
* Returns the current time in milliseconds.
*
* @return String
*/
private static synchronized String getTime() {
callTime = System.currentTimeMillis();
return "" + callTime;
}
/**
* Returns an incremented count of UIDs generated by this instance.
* @return long
*/
private static synchronized long getCount() {
return++uidCounter;
}
/**
* Returns the IP address for this machine.
*
* @throws Exception
* @return String
*/
private static synchronized String getIP() throws Exception {
try {
//lazily obtain IP address
if (null == ipAddress) {
ipAddress = InetAddress.getLocalHost().getHostAddress();
}
return ipAddress;
}
catch (UnknownHostException hostException) {
throw new Exception("Could not determine IP Address.", hostException);
}
}
/**
* Returns an Unique Identifier for this particular Java Virtual Machine.
*
* @return String
*/
private static synchronized String getJVMID() {
//lazily obtain JVM ID
if (null == vmID) {
vmID = new VMID().toString();
}
return vmID;
}
/**
* If multiple UIDGenerators are loaded by different class loaders, they
* will be operating independantly of each other (ie. multiple web
* applications within an application server), It is (remotely) possible
* that multiple UIDGenerator may duplicate their count and random numbers
* within the same millisecond.
*
* @return int
*/
private static synchronized int getClassLoaderId() {
//what ClassLoader within this JVM loaded this UIDGenerator class
int classLoaderId = System.identityHashCode(UIDGenerator.class.
getClassLoader());
return classLoaderId;
}
}