
com.atomikos.util.UniqueIdMgr Maven / Gradle / Ivy
/**
* Copyright (C) 2000-2010 Atomikos
*
* This code ("Atomikos TransactionsEssentials"), by itself,
* is being distributed under the
* Apache License, Version 2.0 ("License"), a copy of which may be found at
* http://www.atomikos.com/licenses/apache-license-2.0.txt .
* You may not use this file except in compliance with the License.
*
* While the License grants certain patent license rights,
* those patent license rights only extend to the use of
* Atomikos TransactionsEssentials by itself.
*
* This code (Atomikos TransactionsEssentials) contains certain interfaces
* in package (namespace) com.atomikos.icatch
* (including com.atomikos.icatch.Participant) which, if implemented, may
* infringe one or more patents held by Atomikos.
* It should be appreciated that you may NOT implement such interfaces;
* licensing to implement these interfaces must be obtained separately from Atomikos.
*
* 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.
*/
package com.atomikos.util;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
/**
*
*
*For managing a set of unique IDs on behalf of a given server
*
*/
public class UniqueIdMgr
{
private static long MAX_PER_EPOCH = 32000;
//max no of txs with same epoch part in xid.
//constructs a unique TID for a particular server
private final static int MAX_LENGTH_OF_NUMERIC_SUFFIX = 16;
String server_; //name of server
long epoch_;//needed to ensure uniqueness
long lastcounter_;
int limit_;
//if positive: indicates max epoch value -> limits number
//of startups of servers
private String prefix_, suffix_;
//prefix and suffix to add to each generated id
private VersionedFile file_;
/**
*Generate a new instance for a given server.
*Assumption: there are never two servers with the same name!
*
*/
public UniqueIdMgr ( String server ) {
this ( server , "./" );
limit_ = -1;
server_ = server;
epoch_ = new Date().getDate();
lastcounter_ = 0;
}
/**
*Preferred constructor: based on file-logged epoch value.
*
*@param server The server's unique name.
*@param directorypath The path (with ending slash!) where the epoch
*file should be written.
*/
public UniqueIdMgr ( String server , String directorypath )
{
this ( server , directorypath , -1 );
}
/**
*Constructor for startup limit.
*Useful for evaluation versions:
*after the epoch reaches the limit, no new
*IDs will be created.
*/
public UniqueIdMgr ( String server, String directorypath, int limit )
{
super();
limit_ = limit;
server_ = server;
file_ = new VersionedFile ( directorypath , server , ".epoch" );
setSuffix ( "" );
setPrefix ( "" );
try {
epoch_ = readEpoch ( );
}
catch ( Exception e ) {
throw new RuntimeException ( e.getMessage() );
}
lastcounter_ = 0;
}
/**
* Sets the suffix to add to each generated ID.
*
* @param suffix The suffix, defaults to empty string.
* This suffix is added directly after the server (base) name
* but is not the last part of the ID.
*/
public void setSuffix ( String suffix )
{
suffix_ = suffix;
}
/**
* Sets the prefix to add to each generated ID.
* @param prefix The prefix, defaults to empty string.
* This value is added at the very beginning of each
* generated ID.
*/
public void setPrefix ( String prefix )
{
prefix_ = prefix;
}
/**
*Read the next epoch value from the epoch file.
*At the same time, this increments the value in the epoch file,
*for next restart.
*
*@return long The next value to use.
*@exception IOException If reading or writing fails.
*/
protected long readEpoch() throws IOException
{
long ret = 0;
try {
FileInputStream fin = file_.openLastValidVersionForReading();
DataInputStream din = new DataInputStream ( fin );
ret = din.readLong();
din.close();
fin.close();
} catch ( FileNotFoundException firstStartup ) {
//merely use the initial epoch value
}
writeEpoch ( ret + 1 );
if ( ret + 1 <= 0 )
throw new RuntimeException ( "Epoch overflow!");
return ret+1 ;
}
/**
*Write the given value into the epoch file.
*@param value The next epoch value.
*@exception IOException On failure.
*/
protected void writeEpoch ( long value ) throws IOException
{
if ( value <= 0 || ( limit_ >= 0 && value > limit_ ) ) {
throw new RuntimeException ( "Epoch overflow!" );
}
FileOutputStream fout = file_.openNewVersionForWriting();
DataOutputStream dout = new DataOutputStream ( fout );
dout.writeLong ( value );
dout.flush();
fout.flush ();
//FIX FOR CASE 31037
fout.getFD ().sync ();
dout.close();
fout.close();
file_.discardBackupVersion();
file_.close();
}
//FIX FOR BUG 10104
private String getCountWithLeadingZeroes ( long number )
{
String ret = Long.toString ( number );
int max = Long.toString ( MAX_PER_EPOCH ).length();
int len = ret.length();
StringBuffer zeroes = new StringBuffer();
while ( len < max ) {
zeroes.append ( "0" );
len++;
}
ret = zeroes.append ( ret ).toString();
return ret;
}
/**
*The main way of obtaining a new UniqueId.
*
*/
public synchronized String get()
{
lastcounter_++;
if ( lastcounter_ > MAX_PER_EPOCH ) {
lastcounter_ = 0;
epoch_++;
try {
writeEpoch ( epoch_ );
}
catch ( Exception e ) {
throw new RuntimeException ( e.getMessage() );
}
}
return getCommonPartOfId() + getCountWithLeadingZeroes ( lastcounter_ ) + getCountWithLeadingZeroes ( epoch_ );
}
private String getCommonPartOfId() {
StringBuffer ret = new StringBuffer(64);
ret.append(prefix_).append(server_).append(suffix_);
return ret.toString();
}
public int getMaxIdLengthInBytes() {
// see case 73086
return getCommonPartOfId().getBytes().length + MAX_LENGTH_OF_NUMERIC_SUFFIX;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy