blob: 007be97d7e083e48325b617c2976ecd548dc3387 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.ode.utils;
import java.math.BigInteger;
import java.net.InetAddress;
import java.util.HashSet;
import java.util.Set;
/**
* This class is used to generate globally unique IDs. The requirements for
* global uniqueness are as follows:
*
* <pre>
* 1) The time on any machine is never set back.
* 2) Each machine has a unique IP address.
* 3) Each process has the 'org.apache.ode.uid.port' property set to the
* same non-zero value.
*
* byte: 0 1 2 3 4 5 6 7 8 9 10 11 12 13
* [ IPADDRESS ] [ START TIME IN MS ] [ count]
* This format allow more compact string representation.
* Persistence mechanism maps 6 bits to a number-char mapping. Byte
* 0-5 (48 bits, 6 bits per char =&gt; 8 chars)
* Since the current time typically has zeros for many of its most significant
* digits, all leading zeros are truncated from the string representation.
* The following 6 bit to char mapping is used:
* 0-9 -&gt; 0-9
* 10-35 -&gt; A-Z
* 36-60 -&gt; a-y
* 61 -&gt; za
* 62 -&gt; zb
* 63 -&gt; zc
* </pre>
*/
public final class GUID implements Cloneable, Comparable, java.io.Serializable {
static final long serialVersionUID = -7977671257884186039L;
static String PROP_PORT = "org.apache.ode.uid.port";
static int port = Integer.getInteger(PROP_PORT, 33666);
// 32 bits
private static final byte[] ipadd = {127,0,0,1};
// 64 bits
private static byte[] baseId = getSystemUniqId();
// 32 bits
private static short cnt = Short.MIN_VALUE;
private static GUID _VM_GUID;
static {
_VM_GUID = new GUID();
}
private final byte[] id;
private transient String guidstring = null;
/**
* Create a new unique GUID
*/
public GUID() {
short c;
byte[] b;
synchronized (GUID.class) {
c = ++cnt;
b = baseId;
if (cnt == Short.MAX_VALUE) {
cnt = Short.MIN_VALUE;
baseId = getSystemUniqId();
}
}
id = new byte[] { ipadd[0], ipadd[1], ipadd[2], ipadd[3], b[7], b[6],
b[5], b[4], b[3], b[2], b[1], b[0], (byte) ((c >>> 8) & 0xff),
(byte) (c & 0xff) };
}
/**
* Reconstitute a GUID from it's string representation
*
* @param str
* DOCUMENTME
*
* @throws MalformedGuidException
* DOCUMENTME
*/
public GUID(String str) throws MalformedGuidException {
if (str == null) {
throw new MalformedGuidException(str);
}
id = new byte[14];
stringToBytes(str);
}
/**
* Get the GUID bytes.
*/
public byte[] getGuid() {
return id;
}
public static GUID getVMGUID() {
return _VM_GUID;
}
public int compareTo(Object o) {
if (o == this) {
return 0;
}
GUID o1 = (GUID) o;
for (short i = 0; i < id.length; ++i) {
if (o1.id[i] < id[i]) {
return -1;
} else if (o1.id[i] > id[i]) {
return 1;
}
}
return 0;
}
public boolean equals(Object o) {
try {
return compareTo(o) == 0;
} catch (ClassCastException ce) {
return false;
}
}
public int hashCode() {
int ret = 0;
for (short i = 0; i < id.length; ++i) {
ret ^= (id[i] << (i % 4));
}
return ret;
}
public static void main(String[] argv) throws Exception {
Set<GUID> set = new HashSet<GUID>();
for (int i = 0; i < 100000; ++i) {
GUID g = new GUID();
if (set.contains(g)) {
System.out.println("CONFLICT>>>");
}
set.add(g);
GUID ng = new GUID(g.toString());
if (!ng.toString().equals(g.toString()) || !ng.equals(g)) {
System.out.println("INEQUALITY>>>");
System.out.println(ng.toString());
System.out.println(g.toString());
} else {
System.out.println(g.toString());
}
}
}
/**
* Convert a GUID to it's string representation. This will return a string
* of at most 32 bytes.
*
* @return DOCUMENTME
*/
public String toString() {
if (guidstring == null) {
guidstring = mapBytesToChars();
}
return guidstring;
}
private static byte[] getSystemUniqId() {
ProcessMutex pm = new ProcessMutex(port);
try {
pm.lock();
} catch (InterruptedException ie) {
System.err
.println("ERROR: Could not establish unique starttime using\n"
+ " TCP port "
+ port
+ " for synchronization. \n"
+ " Perhaps this port is used by anotherprocess? \n"
+ " Check the '"
+ PROP_PORT
+ "' JAVA system property. \n");
throw new RuntimeException("GUID.getSystemUniqId() FAILED!!!");
}
long uid = System.currentTimeMillis();
pm.unlock();
return new byte[] { (byte) (uid & 0xff), (byte) ((uid >>> 8) & 0xff),
(byte) ((uid >>> 16) & 0xff), (byte) ((uid >>> 24) & 0xff),
(byte) ((uid >>> 32) & 0xff), (byte) ((uid >>> 40) & 0xff),
(byte) ((uid >>> 48) & 0xff), (byte) ((uid >>> 56) & 0xff) };
}
private String mapBytesToChars() {
BigInteger bigInt = new BigInteger(id);
return bigInt.toString(34);
}
private void stringToBytes(String s) {
BigInteger bigInt = new BigInteger(s, 34);
byte[] bytes = bigInt.toByteArray();
for (int i = 0; i < id.length; ++i)
id[i] = bytes[i];
}
public static class MalformedGuidException extends Exception {
private static final long serialVersionUID = -8922336058603571809L;
public MalformedGuidException(String guid) {
super("Malformed guid: " + guid);
}
}
}