| ContextIDGenerator.java |
/*
* $Id: ContextIDGenerator.java,v 1.32 2012/02/28 18:10:54 agoubard Exp $
*
* See the COPYRIGHT file for redistribution and use restrictions.
*/
package org.xins.server;
import java.util.Map;
import java.util.Random;
import org.xins.common.MandatoryArgumentChecker;
import org.xins.common.collections.InvalidPropertyValueException;
import org.xins.common.collections.MissingRequiredPropertyException;
import org.xins.common.manageable.Manageable;
import org.xins.common.manageable.InitializationException;
import org.xins.common.net.IPAddressUtils;
import org.xins.common.text.DateConverter;
import org.xins.common.text.TextUtils;
/**
* Generator for diagnostic context identifiers. Generated context
* identifiers will be in the format:
*
* <blockquote><em>app</em>@<em>host</em>:<em>time</em>:<em>rnd</em></blockquote>
*
* where:
*
* <ul>
* <li><em>app</em> is the name of the deployed application, e.g.
* <code>"sso"</code>;
*
* <li><em>host</em> is the hostname of the computer running this
* engine, e.g. <code>"freddy.bravo.com"</code>;
*
* <li><em>time</em> is the current date and time in the format
* <code>yyMMdd-HHmmssNNN</code>, e.g. <code>"050806-171522358"</code>;
*
* <li><em>rnd</em> is a 5 hex-digits randomly generated number, e.g.
* <code>"2f4e6"</code>.
* </ul>
*
* @version $Revision: 1.32 $ $Date: 2012/02/28 18:10:54 $
* @author <a href="mailto:ernst@ernstdehaan.com">Ernst de Haan</a>
*/
final class ContextIDGenerator extends Manageable {
/**
* The hexadecimal digits.
*/
private static final char[] HEX_DIGITS = new char[] {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
};
/**
* The name of the runtime property that hostname for the server
* running the API.
*/
private static final String HOSTNAME_PROPERTY = "org.xins.server.hostname";
/**
* The name of the API. Never <code>null</code>.
*/
private final String _apiName;
/**
* The name for the local host. Never <code>null</code>.
*/
private String _hostname;
/**
* The fixed prefix for generated context identifiers, as a character
* buffer. Never <code>null</code> when this instance is initialized.
*/
private char[] _prefixBuffer;
/**
* The length of the prefix.
*/
private int _prefixLength;
/**
* A date converter. Never <code>null</code>. Needs to be locked before
* usage.
*/
private final DateConverter _dateConverter;
/**
* A pseudo-random number generator. Never <code>null</code>
*/
private final Random _random;
/**
* Constructs a new <code>ContextIDGenerator</code>.
*
* @param apiName
* the name of the API, cannot be <code>null</code>.
*
* @throws IllegalArgumentException
* if <code>apiName == null</code>.
*/
ContextIDGenerator(String apiName)
throws IllegalArgumentException {
// Check preconditions
MandatoryArgumentChecker.check("apiName", apiName);
// Store API name and determine host name
_apiName = apiName;
_hostname = IPAddressUtils.getLocalHost();
// Create a DateConverter that will not prepend the century
_dateConverter = new DateConverter(false);
// Initialize a pseudo-random number generator
_random = new Random();
}
/**
* Performs the initialization procedure (actual implementation). When this
* method is called from {@link #init(Map)}, the state and the
* argument will have been checked and the state will have been set to
* {@link #INITIALIZING}.
*
* @param properties
* the initialization properties, not <code>null</code>.
*
* @throws MissingRequiredPropertyException
* if a required property is not given.
*
* @throws InvalidPropertyValueException
* if the value of a certain property is invalid.
*
* @throws InitializationException
* if the initialization failed, for any other reason.
*/
protected void initImpl(Map<String, String> properties)
throws MissingRequiredPropertyException,
InvalidPropertyValueException,
InitializationException {
// Determine if the hostname has changed
String hostname = properties.get(HOSTNAME_PROPERTY);
if (!TextUtils.isEmpty(hostname) && !hostname.equals(_hostname)) {
Log.log_3310(_hostname, hostname);
_hostname = hostname;
}
// Determine prefix and total context ID length
String prefix = _apiName + '@' + _hostname + ':';
_prefixBuffer = prefix.toCharArray();
_prefixLength = prefix.length();
}
/**
* Generates a diagnostic context identifier.
*
* @return
* the generated diagnostic context identifier, never <code>null</code>.
*
* @throws IllegalStateException
* if this object is currently not usable, i.e. in the
* {@link #USABLE} state.
*/
String generate() throws IllegalStateException {
// Check preconditions
assertUsable();
// Construct a new string buffer with the exact needed capacity
int prefixLength = _prefixLength;
int length = prefixLength + 22;
char[] buffer = new char[length];
// Copy the template into the buffer
System.arraycopy(_prefixBuffer, 0, buffer, 0, prefixLength);
// Determine the current time and append the timestamp
long date = System.currentTimeMillis();
synchronized (_dateConverter) {
_dateConverter.format(date, buffer, prefixLength);
}
// Append 5 pseudo-random hex digits
int random = _random.nextInt() & 0x0fffffff;
int pos = prefixLength + 16;
buffer[pos++] = ':';
buffer[pos++] = HEX_DIGITS[ random & 15];
buffer[pos++] = HEX_DIGITS[(random >> 4) & 15];
buffer[pos++] = HEX_DIGITS[(random >> 8) & 15];
buffer[pos++] = HEX_DIGITS[(random >> 12) & 15];
buffer[pos ] = HEX_DIGITS[(random >> 16) & 15];
// Log and return the context ID
return new String(buffer);
}
}