package er.extensions.eof;
import java.net.InetAddress;
import java.net.UnknownHostException;
import com.webobjects.appserver.WOApplication;
import com.webobjects.eocontrol.EOGlobalID;
import com.webobjects.foundation.NSDictionary;
import com.webobjects.foundation.NSForwardException;
/**
* Experimental class to have quasi-GUIDs that fit into a long. Used as an
* alternative to the ERXLongPrimaryKeyFactory or the 24 byte built-in keys. To
* have it installed, set your pk prototype to "longId". <br>
* Note: by default, the key is partitioned by the lower 2 bytes host address,
* the lower byte of the port number and the current time in seconds. Then you
* have a byte left for inserts in one second. <br>
* This means, if you you are inserting - say - one thousand objects and your
* app crashes and restarts in 4 seconds and directly inserts new objects, then
* you might end up with duplicate keys. So this might only be for apps that stay at a
* low volume.<br>
* Also you need to be sure that your app is in one class B subnet and your
* instances port numbers are partitionable over the lower byte - which means
* less than 256 instances on one host.<br>
* Given these restrictions, you might want to stay with sequences...
*
* @author ak
*
*/
public class ERXTemporaryGlobalID extends EOGlobalID {
/**
* Do I need to update serialVersionUID?
* See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the
* <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a>
*/
private static final long serialVersionUID = 1L;
private static long _cnt;
private static long _lastTime;
private static long _identifier;
private long _value;
public ERXTemporaryGlobalID() {
synchronized (ERXTemporaryGlobalID.class) {
if(_identifier == 0) {
setDefaultAppIdentifier();
}
_value = 0L;
long current = (System.currentTimeMillis()/1000);
if (_lastTime < current) {
_cnt = 0;
_lastTime = current;
}
_value |= ((identifier() << 40) & 0xffffff0000000000L);
_value |= ((_lastTime << 8) & 0x000000ffffffff00L);
_value |= ((_cnt << 0) & 0x00000000000000ffL);
_cnt = _cnt+1;
if (_cnt == 256) {
_lastTime++;
_cnt = 0;
}
}
}
private static void setDefaultAppIdentifier() {
long result = hostIdentifier();
result |= appIdentifier();
_identifier = result;
}
private static long hostIdentifier() {
try {
byte[] address = InetAddress.getLocalHost().getAddress();
long result = address[2] << 16;
result |= address[3] << 8;
return result;
}
catch (UnknownHostException e) {
throw NSForwardException._runtimeExceptionForThrowable(e);
}
}
private static long appIdentifier() {
return WOApplication.application().port().intValue() & 0xff;
}
public static void setIdentifier(int value) {
_identifier = value;
}
private long identifier() {
return _identifier;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ERXTemporaryGlobalID) {
ERXTemporaryGlobalID gid = (ERXTemporaryGlobalID) obj;
return gid._value == _value;
}
return false;
}
@Override
public boolean isTemporary() {
return true;
}
@Override
public int hashCode() {
return (int)_value;
}
public long value() {
return _value;
}
/**
* Returns a pk-ready dictionary with the supplied key.
* @param key
*/
public NSDictionary dictionary(String key) {
return new NSDictionary(value(), key);
}
private static String hex = "0123456789abcdef";
@Override
public String toString() {
StringBuilder s = new StringBuilder();
for(int i = 0; i < 16; i++) {
int index = (int) (_value >> ((15 - i) * 4));
s.append(hex.charAt(index & 0xf));
}
return "0x" + s;
}
}