package org.rzo.netty.ahessian.session; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Random; import org.rzo.netty.ahessian.Constants; /** * A factory for creating Session objects. * Session id generator: taken from apache tomcat. */ class SessionFactory { /** * The default message digest algorithm to use. * TODO: enable user configured algorithms */ private static final String DEFAULT_ALGORITHM = "MD5"; private Random random; private String entropy; private int sessionIdLength = 16; // number of duplicated session ids - anything >0 means we have problems private int duplicates=0; private MessageDigest digest; /** Assignment of id to sessions, to avoid duplicates */ private static Map<String, Session> _sessions = Collections.synchronizedMap(new HashMap<String, Session>()); /** * Creates a new Session object. * If the given id is null a new id is generated * TODO handle id duplicates * * @param id the given id * * @return the session object */ public Session createSession(String id) { if (id == null) id = generateSessionId(); Session session = new SessionImpl(id); _sessions.put(id, session); return session; } /** * Generate and return a new session identifier. * Taken from Tomcat * TODO */ private synchronized String generateSessionId() { byte random[] = new byte[16]; String jvmRoute = getJvmRoute(); String result = null; // Render the result as a String of hexadecimal digits StringBuffer buffer = new StringBuffer(); do { int resultLenBytes = 0; if (result != null) { buffer = new StringBuffer(); duplicates++; } while (resultLenBytes < this.sessionIdLength) { getRandomBytes(random); random = getDigest().digest(random); for (int j = 0; j < random.length && resultLenBytes < this.sessionIdLength; j++) { byte b1 = (byte) ((random[j] & 0xf0) >> 4); byte b2 = (byte) (random[j] & 0x0f); if (b1 < 10) buffer.append((char) ('0' + b1)); else buffer.append((char) ('A' + (b1 - 10))); if (b2 < 10) buffer.append((char) ('0' + b2)); else buffer.append((char) ('A' + (b2 - 10))); resultLenBytes++; } } if (jvmRoute != null) { buffer.append('.').append(jvmRoute); } result = buffer.toString(); } while (_sessions.containsKey(result)); return (result); } private MessageDigest getDigest() { if (this.digest == null) { try { this.digest = MessageDigest.getInstance(DEFAULT_ALGORITHM); } catch (NoSuchAlgorithmException e) { Constants.ahessianLogger.warn("", e); } } return digest; } private String getJvmRoute() { return System.getProperty("jvmRoute"); } private void getRandomBytes(byte bytes[]) { getRandom().nextBytes(bytes); } /** * Return the random number generator instance we should use for * generating session identifiers. If there is no such generator * currently defined, construct and seed a new one. * * @return the random */ private Random getRandom() { if (this.random == null) { // Calculate the new random number generator seed long seed = System.currentTimeMillis(); long t1 = seed; char entropy[] = getEntropy().toCharArray(); for (int i = 0; i < entropy.length; i++) { long update = ((byte) entropy[i]) << ((i % 8) * 8); seed ^= update; } this.random = new java.util.Random(); this.random.setSeed(seed); } return (this.random); } /** * Return the entropy increaser value, or compute a semi-useful value * if this String has not yet been set. * * @return the entropy */ private String getEntropy() { // Calculate a semi-useful value if this has not been set if (entropy == null) { entropy = this.toString(); } return entropy; } /** * Gets the session-id length. * * @return the session id length */ public int getSessionIdLength() { return sessionIdLength; } /** * Gets the session for a given id. * * @param id the id * * @return the session */ public Session getSession(String id) { return _sessions.get(id); } public Session removeSession(String id) { return _sessions.remove(id); } }