package org.jboss.fresh.util;
import java.lang.StringBuffer;
/**
* @author $Author: boky $
* @version $Revision: 4634 $
*/
public final class UniqueNumberGenerator {
protected static long lastUserUniqueIDTime = Long.MIN_VALUE;
/**
* This method returns a string of random 36-based numbers thats exacly <code>length</code> long.
* @param length Required length of the string
* @return A string of random 36-base based numbers with a length of <code>length</code>.
*/
public static String getRandomValue(int length) {
StringBuffer result = new StringBuffer();
for (int i = 0; i < length; i++) {
int v = Math.abs(RandomGUID.myRand.nextInt()) % 36;
result.append(Long.toString(v, 36));
}
return result.toString();
}
/**
* Same as calling {@link #getRandomValue} only that it uses the {@link java.security.SecureRandom}
* for randomness. It is about 3.5-times slower, so use it only when you need really strong
* randomness!
* @see #getRandomValue
*/
public static String getStrongRandomValue(int length) {
StringBuffer result = new StringBuffer();
for (int i = 0; i < length; i++) {
int v = Math.abs(RandomGUID.mySecureRand.nextInt()) % 36;
result.append(Long.toString(v, 36));
}
return result.toString();
}
/**
* This is a 'lite' version of {@link #getUniqueID}. While this value is should be globally
* unique, it is not guaranteed. It is much shorter and therefore chances of same
* two values appearing is much greater. If you use it and want it to be server-unique,
* make sure that it is not called more that once in each milisecond.
* @return the random value
*/
public static String getRandomID() {
long d = System.currentTimeMillis(); // retrieve current date
String D = Long.toString(d, 36); // Compact it as much as possible (use the highest available base)
String id; // generated id
while (D.length() < 4) { // The stamp will not be longer than 12 characters
D = '0' + D;
} // start with the current time stamp
StringBuffer sb = new StringBuffer();
int i = Math.abs(RandomGUID.myRand.nextInt());
sb.append(Integer.toString(i, 16));
sb.append(getRandomValue(sb.length()-8));
sb.append('-').append(D.substring(D.length() - 4));
id = sb.toString();
return id;
}
/**
* Same as calling {@link #getRandomID} only that it uses the {@link java.security.SecureRandom}
* for randomness. It is about 3.5-times slower, so use it only when you need really strong
* randomness!
* @see #getRandomID
*/
public static String getSecureRandomID() {
long d = System.currentTimeMillis(); // retrieve current date
String D = Long.toString(d, 36); // Compact it as much as possible (use the highest available base)
String id; // generated id
while (D.length() < 4) { // The stamp will not be longer than 12 characters
D = '0' + D;
} // start with the current time stamp
StringBuffer sb = new StringBuffer();
int i = Math.abs(RandomGUID.mySecureRand.nextInt());
sb.append(Integer.toString(i, 16));
sb.append(getRandomValue(sb.length()-8));
sb.append('-').append(D.substring(D.length() - 4));
id = sb.toString();
return id;
}
/**
* Convenience shorthand method for {@link #getRandomID} and {@link #getSecureRandomID}. Essentially
* returns the result of this evaluation: <code>secure ? getSecureRandomID() : getRandomID()</code>.
* @param secure Tell whether to return secure or nonsecure-generated ID.
* @return The same as calling {@link #getRandomID} or {@link #getSecureRandomID}
* @see #getRandomID
* @see #getSecureRandomID
*/
public static String getRandomID(boolean secure) {
return secure ? getSecureRandomID() : getRandomID();
}
/**
* <p>This method will create a unique (quite short) identifier, which may be used be presented to the
* end user. How is this different from {@link #getUniqueID()} and {@link #getRandomID()} and
* {@link #getGUID()}? Well in the following:<ul>
* <li>Careful consideration has been put in making the number as short as possible</li>
* <li>It is not guaranteed to be word-wide unique (but will be locally unique)</li>
* <li>It includes a checksum so you may check for its validness using {@link #isValidUserUniqueID}</li>
* <li>It's user presentable</li>
* </ul></p>
* <p><strong>Note:</strong> As with most random number generators, this method also uses <em>system's
* current time</em> only that it's even more dependant on it. So <strong>double check</strong> that
* your system time is set correctly to avoid duplication of IDs. There is a small safety built in
* (the chances of collission are 1:1679616 if you modify your system time, they drop to around 1:1296
* if you don't restart your JVM after changing the time), but don't you be counting very much on it.</p>
*
* @return A new, unique, {@link String}.
*/
public static synchronized String getUserUniqueID() {
long d = System.currentTimeMillis();
while(d==lastUserUniqueIDTime) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// do nothing
}
// make sure it's unike
d = System.currentTimeMillis();
}
lastUserUniqueIDTime = d;
String D = Long.toString(d, 36);
while (D.length() < 8) {
D = '0' + D;
}
StringBuffer sb = new StringBuffer(RandomGUID.myShortCode);
sb.append(D);
sb.append(getRandomValue(2));
sb.append(twoLetterChecksum(sb.toString()));
return sb.toString().toLowerCase();
}
/**
* Check if given string is a valid userUnique ID or not (whether it was
* created by {@link #getUserUniqueID()} or not).
* @param userUniqueID The ID.
* @return If <tt>null</tt> returns <tt>false</tt>, otherwise checks if the
* checksum matches.
*/
public static boolean isValidUserUniqueID(String userUniqueID) {
// todo: check why test for 'dwe98ahsnlga100', 'l0e98akjxrhc100' failed.
if(userUniqueID==null) {
return false;
}
if(userUniqueID.length()!=14) {
return false;
}
final String s = userUniqueID.toLowerCase();
return twoLetterChecksum(s.substring(0, 12)).equalsIgnoreCase(s.substring(12, 14));
}
/**
* Calculate a two-letter checksum from given string. Checksum
* <strong>is not case sensitive</strong>
* @param s The string to calculate checksum from
* @return The calculated checksum
*/
public static String twoLetterChecksum(String s) {
// note: 36^2 = 1296
// 1291 (prime) < 1296 < 1297 (prime)
int sum = 0;
int i = 0;
s = s.toLowerCase();
for(int j = 0; j < s.length(); j++) {
if(++i > 37) {
i = 1;
}
sum += (int) s.charAt(j) * i;
}
String res = Integer.toString(sum % 1291, 36).toLowerCase();
if(res.length() < 2) {
return '0' + res;
} else {
return res;
}
}
/**
* Genaretes an unique ID that ought to be globally unique. Format of the key is
* <code>XXXXXXXXXXXXX-YYYYYY-ZZZZZ</code> where XXXX is the machine id (does not
* change while running on the same JVM on the same computer), YYYY is the random
* key and ZZZZ is the current timestamp.
*
* @return The uniqueID value
*/
public static String getUniqueID() {
// Chances of two processes generating the same ID are astronomical,
// since we use a current timestamp + 8 random bytes from
// SHA1 algorithm.
// 30 bits = 1.073.741.824 different combinations for each milisecond.
long d = System.currentTimeMillis(); // retrieve current date
String D = Long.toString(d, 36); // Compact it as much as possible (use the highest available base)
String id; // generated id
while (D.length() < 6) { // The stamp will not be longer than 12 characters
D = '0' + D;
} // start with the current time stamp
D = D.substring(D.length() - 6);
StringBuffer sb = new StringBuffer(RandomGUID.machineID);
if (RandomGUID.myRand != null) { // and a few random bytes
long l = Math.abs(RandomGUID.myRand.nextLong());
sb.append(Long.toString(l, 32));
sb.append('-');
}
id = sb.append(D).toString();
return id;
}
/**
* Same as calling {@link #getUniqueID} only that it uses the {@link java.security.SecureRandom}
* for randomness. It is about 3.5-times slower, so use it only when you need really strong
* randomness!
* @see #getUniqueID
*/
public static String getSecureUniqueID() {
// Chances of two processes generating the same ID are astronomical,
// since we use a current timestamp + 8 random bytes from
// SHA1 algorithm.
// 30 bits = 1.073.741.824 different combinations for each milisecond.
long d = System.currentTimeMillis(); // retrieve current date
String D = Long.toString(d, 36); // Compact it as much as possible (use the highest available base)
String id; // generated id
while (D.length() < 6) { // The stamp will not be longer than 12 characters
D = '0' + D;
} // start with the current time stamp
D = D.substring(D.length() - 6);
StringBuffer sb = new StringBuffer(RandomGUID.machineID);
if (RandomGUID.mySecureRand != null) { // and a few random bytes
long l = Math.abs(RandomGUID.mySecureRand.nextLong());
sb.append(Long.toString(l, 32));
sb.append('-');
}
id = sb.append(D).toString();
return id;
}
/**
* Convenience shorthand method for {@link #getUniqueID} and {@link #getSecureUniqueID}. Essentially
* returns the result of this evaluation: <code>secure ? getSecureUniqueID() : getUniqueID()</code>.
* @param secure Tell whether to return secure or nonsecure-generated GUID.
* @return The same as calling {@link #getUniqueID} or {@link #getSecureUniqueID}
* @see #getUniqueID
* @see #getSecureUniqueID
*/
public static String getUniqueID(boolean secure) {
return secure ? getSecureUniqueID() : getUniqueID();
}
/**
* Uses {@link RandomGUID} to construct a Globbally Unique Identifier. In general,
* {@link #getUniqueID} will produce faster results. Since Java cannot read MAC address
* of the network card, the current IP address is used. This is combined with
* session information (the startup time of JVM, or more exacly, the first use of this class),
* machine information (JVM version, working folder, JVM folder etc.), some random
* values and current time to generate a globally unique ID. The retrieved number
* <strong>will not</strong> contai any curly braces.
* @return GUID in the standard GUID format (example: C2FEEEAC-CFCD-11D1-8B05-00600806D9B6)
* without the curly braces.
*/
public static String getGUID() {
RandomGUID r = new RandomGUID(false);
return r.toString();
}
/**
* Same as calling {@link #getGUID} only that it uses {@link java.security.SecureRandom}, therefore
* this method is 3.5 times slower!. Use it when you need true randomness.
* @return GUID in the standard GUID format (example: C2FEEEAC-CFCD-11D1-8B05-00600806D9B6)
* without the curly braces.
*/
public static String getSecureGUID() {
RandomGUID r = new RandomGUID(true);
return r.toString();
}
/**
* Convenience shorthand method for {@link #getGUID} and {@link #getSecureGUID}. Essentially returns
* the result of this evaluation: <code>secure ? getSecureGUID() : getGUID()</code>.
* @param secure Tell whether to return secure or nonsecure-generated GUID.
* @return An GUID in standard GUID format (example: C2FEEEAC-CFCD-11D1-8B05-00600806D9B6)
* @see #getGUID
* @see #getSecureGUID
*/
public static String getGUID(boolean secure) {
return secure ? getSecureGUID() : getGUID();
}
}