001: /*
002: * $Id: ContextIDGenerator.java,v 1.28 2007/04/25 14:13:18 agoubard Exp $
003: *
004: * Copyright 2003-2007 Orange Nederland Breedband B.V.
005: * See the COPYRIGHT file for redistribution and use restrictions.
006: */
007: package org.xins.server;
008:
009: import java.util.Random;
010:
011: import org.xins.common.MandatoryArgumentChecker;
012: import org.xins.common.collections.PropertyReader;
013: import org.xins.common.collections.InvalidPropertyValueException;
014: import org.xins.common.collections.MissingRequiredPropertyException;
015: import org.xins.common.manageable.Manageable;
016: import org.xins.common.manageable.InitializationException;
017: import org.xins.common.net.IPAddressUtils;
018: import org.xins.common.text.DateConverter;
019: import org.xins.common.text.TextUtils;
020:
021: /**
022: * Generator for diagnostic context identifiers. Generated context
023: * identifiers will be in the format:
024: *
025: * <blockquote><em>app</em>@<em>host</em>:<em>time</em>:<em>rnd</em></blockquote>
026: *
027: * where:
028: *
029: * <ul>
030: * <li><em>app</em> is the name of the deployed application, e.g.
031: * <code>"sso"</code>;
032: *
033: * <li><em>host</em> is the hostname of the computer running this
034: * engine, e.g. <code>"freddy.bravo.com"</code>;
035: *
036: * <li><em>time</em> is the current date and time in the format
037: * <code>yyMMdd-HHmmssNNN</code>, e.g. <code>"050806-171522358"</code>;
038: *
039: * <li><em>rnd</em> is a 5 hex-digits randomly generated number, e.g.
040: * <code>"2f4e6"</code>.
041: * </ul>
042: *
043: * @version $Revision: 1.28 $ $Date: 2007/04/25 14:13:18 $
044: * @author <a href="mailto:ernst@ernstdehaan.com">Ernst de Haan</a>
045: */
046: final class ContextIDGenerator extends Manageable {
047:
048: /**
049: * The hexadecimal digits.
050: */
051: private static final char[] HEX_DIGITS = new char[] { '0', '1',
052: '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
053: 'e', 'f', };
054:
055: /**
056: * The name of the runtime property that hostname for the server
057: * running the API.
058: */
059: private static final String HOSTNAME_PROPERTY = "org.xins.server.hostname";
060:
061: /**
062: * The name of the API. Never <code>null</code>.
063: */
064: private final String _apiName;
065:
066: /**
067: * The name for the local host. Never <code>null</code>.
068: */
069: private String _hostname;
070:
071: /**
072: * The fixed prefix for generated context identifiers, as a character
073: * buffer. Never <code>null</code> when this instance is initialized.
074: */
075: private char[] _prefixBuffer;
076:
077: /**
078: * The length of the prefix.
079: */
080: private int _prefixLength;
081:
082: /**
083: * A date converter. Never <code>null</code>. Needs to be locked before
084: * usage.
085: */
086: private final DateConverter _dateConverter;
087:
088: /**
089: * A pseudo-random number generator. Never <code>null</code>
090: */
091: private final Random _random;
092:
093: /**
094: * Constructs a new <code>ContextIDGenerator</code>.
095: *
096: * @param apiName
097: * the name of the API, cannot be <code>null</code>.
098: *
099: * @throws IllegalArgumentException
100: * if <code>apiName == null</code>.
101: */
102: ContextIDGenerator(String apiName) throws IllegalArgumentException {
103:
104: // Check preconditions
105: MandatoryArgumentChecker.check("apiName", apiName);
106:
107: // Store API name and determine host name
108: _apiName = apiName;
109: _hostname = IPAddressUtils.getLocalHost();
110:
111: // Create a DateConverter that will not prepend the century
112: _dateConverter = new DateConverter(false);
113:
114: // Initialize a pseudo-random number generator
115: _random = new Random();
116: }
117:
118: /**
119: * Performs the initialization procedure (actual implementation). When this
120: * method is called from {@link #init(PropertyReader)}, the state and the
121: * argument will have been checked and the state will have been set to
122: * {@link #INITIALIZING}.
123: *
124: * @param properties
125: * the initialization properties, not <code>null</code>.
126: *
127: * @throws MissingRequiredPropertyException
128: * if a required property is not given.
129: *
130: * @throws InvalidPropertyValueException
131: * if the value of a certain property is invalid.
132: *
133: * @throws InitializationException
134: * if the initialization failed, for any other reason.
135: */
136: protected void initImpl(PropertyReader properties)
137: throws MissingRequiredPropertyException,
138: InvalidPropertyValueException, InitializationException {
139:
140: // Determine if the hostname has changed
141: String hostname = properties.get(HOSTNAME_PROPERTY);
142: if (!TextUtils.isEmpty(hostname) && !hostname.equals(_hostname)) {
143: Log.log_3310(_hostname, hostname);
144: _hostname = hostname;
145: }
146:
147: // Determine prefix and total context ID length
148: String prefix = _apiName + '@' + _hostname + ':';
149: _prefixBuffer = prefix.toCharArray();
150: _prefixLength = prefix.length();
151: }
152:
153: /**
154: * Generates a diagnostic context identifier.
155: *
156: * @return
157: * the generated diagnostic context identifier, never <code>null</code>.
158: *
159: * @throws IllegalStateException
160: * if this object is currently not usable, i.e. in the
161: * {@link #USABLE} state.
162: */
163: String generate() throws IllegalStateException {
164:
165: // Check preconditions
166: assertUsable();
167:
168: // Construct a new string buffer with the exact needed capacity
169: int prefixLength = _prefixLength;
170: int length = prefixLength + 22;
171: char[] buffer = new char[length];
172:
173: // Copy the template into the buffer
174: System.arraycopy(_prefixBuffer, 0, buffer, 0, prefixLength);
175:
176: // Determine the current time and append the timestamp
177: long date = System.currentTimeMillis();
178: synchronized (_dateConverter) {
179: _dateConverter.format(date, buffer, prefixLength);
180: }
181:
182: // Append 5 pseudo-random hex digits
183: int random = _random.nextInt() & 0x0fffffff;
184: int pos = prefixLength + 16;
185: buffer[pos++] = ':';
186: buffer[pos++] = HEX_DIGITS[random & 15];
187: buffer[pos++] = HEX_DIGITS[(random >> 4) & 15];
188: buffer[pos++] = HEX_DIGITS[(random >> 8) & 15];
189: buffer[pos++] = HEX_DIGITS[(random >> 12) & 15];
190: buffer[pos] = HEX_DIGITS[(random >> 16) & 15];
191:
192: // Log and return the context ID
193: return new String(buffer);
194: }
195: }
|