/*
* JBoss, Home of Professional Open Source
* Copyright 2006, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
* (C) 2005-2006,
* @author JBoss Inc.
*/
/*
* Copyright (C) 1998, 1999, 2000, 2001,
*
* Arjuna Solutions Limited,
* Newcastle upon Tyne,
* Tyne and Wear,
* UK.
*
* $Id: Uid.java 2342 2006-03-30 13:06:17Z $
*/
package com.arjuna.ats.arjuna.common;
import com.arjuna.ats.arjuna.state.InputBuffer;
import com.arjuna.ats.arjuna.state.OutputBuffer;
import com.arjuna.ats.arjuna.utils.Utility;
import java.lang.Cloneable;
import java.io.Serializable;
import java.io.PrintStream;
import com.arjuna.ats.arjuna.exceptions.FatalError;
import java.io.IOException;
import java.net.UnknownHostException;
import java.lang.StringIndexOutOfBoundsException;
import java.lang.NumberFormatException;
import java.lang.CloneNotSupportedException;
import java.util.concurrent.atomic.AtomicInteger;
import com.arjuna.ats.arjuna.logging.tsLogger;
/**
* Implements a unique identity class.
*
* @author Mark Little (mark@arjuna.com)
* @version $Id: Uid.java 2342 2006-03-30 13:06:17Z $
* @since 1.0.
*
* @message com.arjuna.ats.arjuna.common.Uid_1
* [com.arjuna.ats.arjuna.common.Uid_1] - cannot get local host.
* @message com.arjuna.ats.arjuna.common.Uid_2
* [com.arjuna.ats.arjuna.common.Uid_2] - Uid.Uid string constructor
* could not create nullUid
* @message com.arjuna.ats.arjuna.common.Uid_3
* [com.arjuna.ats.arjuna.common.Uid_3] - Uid general parsing error:
* {0}
* @message com.arjuna.ats.arjuna.common.Uid_4
* [com.arjuna.ats.arjuna.common.Uid_4] - Uid.Uid string constructor
* could not create nullUid for incorrect string: {0}
* @message com.arjuna.ats.arjuna.common.Uid_5
* [com.arjuna.ats.arjuna.common.Uid_5] - Uid.Uid string constructor
* incorrect: {0}
* @message com.arjuna.ats.arjuna.common.Uid_6
* [com.arjuna.ats.arjuna.common.Uid_6] - Uid.generateHash called for
* invalid Uid. Will ignore.
* @message com.arjuna.ats.arjuna.common.Uid_7
* [com.arjuna.ats.arjuna.common.Uid_7] - nullUid error for
* @message com.arjuna.ats.arjuna.common.Uid_8
* [com.arjuna.ats.arjuna.common.Uid_8] - Invalid string:
* @message com.arjuna.ats.arjuna.common.Uid_9
* [com.arjuna.ats.arjuna.common.Uid_9] - Invalid Uid object.
* @message com.arjuna.ats.arjuna.common.Uid_10
* [com.arjuna.ats.arjuna.common.Uid_10] - Cannot unpack into nullUid!
*/
public class Uid implements Cloneable, Serializable
{
private static final long serialVersionUID = 7808395904206530189L;
/**
* Create a new instance.
*/
public Uid ()
{
hostAddr = -1;
process = 0;
sec = 0;
other = 0;
_hashValue = -1;
_valid = true;
try
{
hostAddr = Utility.hostInetAddr(); /* calculated only once */
process = Utility.getpid();
if (Uid.initTime == 0)
Uid.initTime = (int) (System.currentTimeMillis() / 1000);
sec = Uid.initTime;
other = Uid.getValue();
}
catch (UnknownHostException e)
{
if (tsLogger.arjLoggerI18N.isWarnEnabled())
tsLogger.arjLoggerI18N.warn("com.arjuna.ats.arjuna.common.Uid_1");
_valid = false;
}
}
/**
* Create a copy of the specified identifier.
*/
public Uid (Uid copyFrom)
{
copy(copyFrom);
}
/**
* Create Uid from string representation. If the string does not represent a
* valid Uid then the instance will be set to nullUid.
*/
public Uid (String uidString)
{
this(uidString, true);
}
/**
* Create Uid from string representation. boolean arg says whether to give
* up if an error is detected or to simply replace with nullUid.
*/
public Uid (String uidString, boolean errsOk)
{
char theBreakChar = Uid.getBreakChar(uidString);
hostAddr = -1;
process = 0;
sec = 0;
other = 0;
_hashValue = -1;
_valid = false;
if (uidString.length() > 0)
{
int startIndex = 0;
int endIndex = 0;
String s = null;
try
{
while (uidString.charAt(endIndex) != theBreakChar)
endIndex++;
s = uidString.substring(startIndex, endIndex);
hostAddr = (int) Utility.hexStringToLong(s);
startIndex = endIndex + 1;
endIndex++;
while (uidString.charAt(endIndex) != theBreakChar)
endIndex++;
s = uidString.substring(startIndex, endIndex);
process = (int) Utility.hexStringToLong(s);
startIndex = endIndex + 1;
endIndex++;
while (uidString.charAt(endIndex) != theBreakChar)
endIndex++;
s = uidString.substring(startIndex, endIndex);
sec = (int) Utility.hexStringToLong(s);
s = uidString.substring(endIndex + 1, uidString.length());
other = (int) Utility.hexStringToLong(s);
_valid = true;
}
catch (NumberFormatException e)
{
if (!errsOk)
{
if (tsLogger.arjLoggerI18N.isWarnEnabled())
{
tsLogger.arjLoggerI18N.warn("com.arjuna.ats.arjuna.common.Uid_3", new Object[]
{ uidString });
}
}
_valid = false;
}
catch (StringIndexOutOfBoundsException e)
{
_valid = false;
}
}
else
{
this.copy(Uid.nullUid());
}
if (!_valid)
{
if (errsOk)
{
try
{
this.copy(Uid.nullUid());
}
catch (Exception e)
{
if (tsLogger.arjLoggerI18N.isFatalEnabled())
{
tsLogger.arjLoggerI18N.fatal("com.arjuna.ats.arjuna.common.Uid_4", new Object[]
{ uidString });
}
throw new FatalError(
tsLogger.log_mesg.getString("com.arjuna.ats.arjuna.common.Uid_2"));
}
}
else
{
if (tsLogger.arjLoggerI18N.isFatalEnabled())
{
tsLogger.arjLoggerI18N.fatal("com.arjuna.ats.arjuna.common.Uid_5", new Object[]
{ uidString });
}
throw new FatalError(
tsLogger.log_mesg.getString("com.arjuna.ats.arjuna.common.Uid_3")
+ uidString);
}
}
}
/**
* Override Object.hashCode. We always return a positive value.
*/
/*
* generateHash should have been called by now.
*/
public int hashCode ()
{
if (_hashValue == -1)
generateHash();
return _hashValue;
}
/**
* Routines to pack and unpack Uids into Buffers (for RPC, storage etc)
*/
public synchronized void pack (OutputBuffer packInto) throws IOException
{
if (_valid)
{
packInto.packInt(hostAddr);
packInto.packInt(process);
packInto.packInt(sec);
packInto.packInt(other);
}
else
throw new IOException(
tsLogger.log_mesg.getString("com.arjuna.ats.arjuna.common.Uid_9"));
}
public synchronized void unpack (InputBuffer unpackFrom) throws IOException
{
/*
* Prevent nullUid being overwritten by unpack.
*/
if (this == Uid.nullUid())
throw new IOException(
tsLogger.log_mesg.getString("com.arjuna.ats.arjuna.common.Uid_10"));
_valid = true; // should not be able to pack an invalid Uid.
try
{
hostAddr = unpackFrom.unpackInt();
process = unpackFrom.unpackInt();
sec = unpackFrom.unpackInt();
other = unpackFrom.unpackInt();
_hashValue = -1;
}
catch (IOException e)
{
_valid = false;
throw e;
}
}
/**
* Print a human-readable form of the Uid.
*/
public void print (PrintStream strm)
{
strm.print("<Uid:" + this.toString() + ">");
}
public String stringForm ()
{
return Utility.intToHexString(hostAddr) + Uid.breakChar
+ Utility.intToHexString(process) + Uid.breakChar
+ Utility.intToHexString(sec) + Uid.breakChar
+ Utility.intToHexString(other);
}
/**
* @return a string representation of the Uid with all : replaced by _ so
* that the name may be used as a file name.
*/
public String fileStringForm ()
{
return Utility.intToHexString(hostAddr) + Uid.fileBreakChar
+ Utility.intToHexString(process) + Uid.fileBreakChar
+ Utility.intToHexString(sec) + Uid.fileBreakChar
+ Utility.intToHexString(other);
}
/**
* Same as stringForm()
*/
public String toString ()
{
return stringForm();
}
/**
* Create a copy of this instance.
*/
public Object clone () throws CloneNotSupportedException
{
return new Uid(this);
}
/**
* Copy the specified Uid over this instance.
*/
public synchronized void copy (Uid toCopy)
{
if (toCopy == this)
return;
hostAddr = toCopy.hostAddr;
process = toCopy.process;
sec = toCopy.sec;
other = toCopy.other;
_hashValue = toCopy._hashValue;
_valid = toCopy._valid;
}
/**
* Uid comparisons.
*/
/**
* Override Object.equals
*/
public boolean equals (Object o)
{
if (o instanceof Uid)
return this.equals((Uid) o);
else
return false;
}
public boolean equals (Uid u)
{
if (u == null)
return false;
if (u == this)
return true;
return ((other == u.other) && (sec == u.sec) && (process == u.process) && (hostAddr == u.hostAddr));
}
public boolean notEquals (Uid u)
{
if (u == null)
return true;
if (u == this)
return false;
return ((other != u.other) || (sec != u.sec) || (process != u.process) || (hostAddr != u.hostAddr));
}
public boolean lessThan (Uid u)
{
if (u == null)
return false;
if (u == this)
return false;
if (this.equals(u))
return false ;
if (LAST_RESOURCE_UID.equals(this))
return false ;
if (LAST_RESOURCE_UID.equals(u))
return true ;
if (hostAddr < u.hostAddr)
return true;
else
if (hostAddr == u.hostAddr)
{
if (process < u.process)
return true;
else
if (process == u.process)
{
if (sec < u.sec)
return true;
else
if ((sec == u.sec) && (other < u.other))
return true;
}
}
return false;
}
public boolean greaterThan (Uid u)
{
if (u == null)
return false;
if (u == this)
return false;
if (this.equals(u))
return false ;
if (LAST_RESOURCE_UID.equals(this))
return true ;
if (LAST_RESOURCE_UID.equals(u))
return false ;
if (hostAddr > u.hostAddr)
return true;
else
if (hostAddr == u.hostAddr)
{
if (process > u.process)
return true;
else
if (process == u.process)
{
if (sec > u.sec)
return true;
else
if ((sec == u.sec) && (other > u.other))
return true;
}
}
return false;
}
/**
* Is the Uid valid?
*/
public synchronized final boolean valid ()
{
return _valid;
}
/**
* Return a null Uid (0:0:0:0)
*/
public static final Uid nullUid ()
{
return NIL_UID;
}
/**
* Return a last resource Uid (0:0:0:1)
*/
public static final Uid lastResourceUid ()
{
return LAST_RESOURCE_UID;
}
/**
* Return the maximum Uid (7fffffff:7fffffff:7fffffff:7fffffff)
*/
public static final Uid maxUid ()
{
return MAX_UID;
}
/**
* Return the minimum Uid (-80000000:-80000000:-80000000:-80000000)
*/
public static final Uid minUid ()
{
return MIN_UID;
}
/*
* Serialization methods. Similar to buffer packing. If the Uid is invalid
* the an IOException is thrown.
*
* @since JTS 2.1.
*/
private void writeObject (java.io.ObjectOutputStream out)
throws IOException
{
if (_valid)
{
out.writeInt(hostAddr);
out.writeInt(process);
out.writeInt(sec);
out.writeInt(other);
}
else
throw new IOException("Invalid Uid object.");
}
/*
* Serialization methods. Similar to buffer unpacking. If the
* unserialization fails then an IOException is thrown.
*
* @since JTS 2.1.
*/
private void readObject (java.io.ObjectInputStream in) throws IOException,
ClassNotFoundException
{
_valid = true; // should not be able to pack an invalid Uid.
try
{
hostAddr = in.readInt();
process = in.readInt();
sec = in.readInt();
other = in.readInt();
_hashValue = -1; // Set hashvalue to -1 to force a regeneration
// of the hashcode
}
catch (IOException e)
{
_valid = false;
throw e;
}
}
private static final int MAX_SEQ_VALUE = 0x40000000; // 2^30, which is a bit conservative.
private static int getValue ()
{
int value = 0;
do {
value = uidsCreated.getAndIncrement();
if(value == MAX_SEQ_VALUE) {
uidsCreated.set(0);
initTime = (int) (System.currentTimeMillis() / 1000);
}
} while(value >= MAX_SEQ_VALUE);
return value;
}
/*
* Is an idempotent operation, so can be called more than once without
* adverse effect.
*/
private final synchronized void generateHash ()
{
if (_valid)
{
if (true)
_hashValue = hostAddr ^ process ^ sec ^ other;
else
{
int g = 0;
String p = toString();
int len = p.length();
int index = 0;
if (len > 0)
{
while (len-- > 0)
{
_hashValue = (_hashValue << 4)
+ (int) (p.charAt(index));
g = _hashValue & 0xf0000000;
if (g > 0)
{
_hashValue = _hashValue ^ (g >> 24);
_hashValue = _hashValue ^ g;
}
index++;
}
}
}
/*
* Integers are signed in Java, so we have to be careful and only
* return a signed value.
*/
if (_hashValue < 0)
_hashValue = -_hashValue;
}
else
{
if (tsLogger.arjLoggerI18N.isWarnEnabled())
tsLogger.arjLoggerI18N.warn("com.arjuna.ats.arjuna.common.Uid_6");
}
}
/*
* Since we may be given a Uid from the file system (which uses '_' to
* separate fields, we need to be able to convert.
*/
private static final char getBreakChar (String uidString)
{
if (uidString == null)
return Uid.breakChar;
if (uidString.indexOf(fileBreakChar) != -1)
return Uid.fileBreakChar;
else
return Uid.breakChar;
}
// use ints rather than longs for interoperability with C++ version
private volatile int hostAddr;
private volatile int process;
private volatile int sec;
private volatile int other;
private volatile int _hashValue;
private volatile boolean _valid;
private static final AtomicInteger uidsCreated = new AtomicInteger();
private static volatile int initTime ;
private static final char breakChar = ':';
private static final char fileBreakChar = '_';
private static final Uid NIL_UID = new Uid("0:0:0:0") ;
private static final Uid LAST_RESOURCE_UID = new Uid("0:0:0:1") ;
private static final Uid MAX_UID = new Uid("7fffffff:7fffffff:7fffffff:7fffffff") ;
private static final Uid MIN_UID = new Uid("-80000000:-80000000:-80000000:-80000000") ;
}