001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package com.terracotta.session.util;
006:
007: import com.tc.object.bytecode.Manager;
008: import com.terracotta.session.SessionId;
009:
010: import java.security.SecureRandom;
011:
012: /**
013: * generates session cookie id of the format: <4hex:random><4hex:nextId>[[16hex:random]*]
014: */
015: public class DefaultIdGenerator implements SessionIdGenerator {
016:
017: // NOTE: IMPORTANT!!! don't change MIN_LENGTH without reviewing generateKey method
018: private static final int MIN_LENGTH = 8;
019:
020: private static final char[] HEXCHARS = new char[] { '0', '1', '2',
021: '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
022: 'F' };
023:
024: private final SecureRandom random;
025: private final int idLength;
026: private final String serverId;
027: private final String delimiter;
028: private final int lockType;
029: private short nextId = Short.MIN_VALUE;
030:
031: public static SessionIdGenerator makeInstance(ConfigProperties cp,
032: int lockType) {
033: Assert.pre(cp != null);
034: final int idLength = cp.getSessionIdLength();
035: final String serverId = cp.getServerId();
036: final String delimiter = cp.getDelimiter();
037: return new DefaultIdGenerator(idLength, serverId, lockType,
038: delimiter);
039: }
040:
041: // for non-synchronous-write tests
042: public DefaultIdGenerator(final int idLength, final String serverId) {
043: this (idLength, serverId, Manager.LOCK_TYPE_WRITE,
044: ConfigProperties.defaultDelimiter);
045: }
046:
047: public DefaultIdGenerator(final int idLength,
048: final String serverId, int lockType, String delimiter) {
049: random = new SecureRandom();
050: // init
051: random.nextInt();
052:
053: this .lockType = lockType;
054: this .idLength = Math.max(idLength, MIN_LENGTH);
055: this .serverId = serverId;
056: this .delimiter = delimiter;
057: }
058:
059: public SessionId generateNewId() {
060: final String key = generateKey();
061: final String externalId = makeExternalId(key);
062: return new DefaultSessionId(key, null, externalId, lockType,
063: false);
064: }
065:
066: public SessionId makeInstanceFromBrowserId(String requestedSessionId) {
067: Assert.pre(requestedSessionId != null);
068: final int dlmIndex = getDLMIndex(requestedSessionId);
069: // everything before dlmIndex is key, everything after is serverId
070: if (dlmIndex > 0) {
071: final String key = requestedSessionId
072: .substring(0, dlmIndex);
073: final String requestedServerId = requestedSessionId
074: .substring(dlmIndex + 1);
075: final String externalId = makeExternalId(key);
076: return new DefaultSessionId(key, requestedSessionId,
077: externalId, lockType, !requestedServerId
078: .equals(serverId));
079: } else {
080: // DLM is missing. someone is messing with our session ids!
081: return null;
082: }
083: }
084:
085: protected int getDLMIndex(String requestedSessionId) {
086: return requestedSessionId.indexOf(delimiter);
087: }
088:
089: protected String makeExternalId(String key) {
090: String externalId = key + delimiter + serverId;
091: return externalId;
092: }
093:
094: protected String getDelimiter() {
095: return delimiter;
096: }
097:
098: protected String getServerId() {
099: return serverId;
100: }
101:
102: protected int getLockType() {
103: return lockType;
104: }
105:
106: public SessionId makeInstanceFromInternalKey(String key) {
107: final String externalId = makeExternalId(key);
108: return new DefaultSessionId(key, externalId, externalId,
109: lockType, false);
110: }
111:
112: /**
113: * NOTE: IMPORTANT!!! don't change MIN_LENGTH without reviewing generateKey method mininum size of generated string
114: * must be 8 chars
115: */
116: protected synchronized String generateKey() {
117: final byte[] bytes = new byte[2];
118: final StringBuffer sb = new StringBuffer();
119: // append 4hex:random
120: random.nextBytes(bytes);
121: sb.append(toHex(bytes, 2));
122:
123: // append 4hex:nextId
124: toBytes(getNextId(), bytes);
125: sb.append(toHex(bytes, 2));
126:
127: // append random bytes until we reach required length
128: if (sb.length() < idLength) {
129: final byte[] extraBytes = new byte[idLength - MIN_LENGTH];
130: random.nextBytes(extraBytes);
131: sb.append(toHex(extraBytes, extraBytes.length));
132: }
133: return sb.substring(0, idLength);
134: }
135:
136: protected synchronized short getNextId() {
137: return nextId++;
138: }
139:
140: protected static void toBytes(short s, byte[] bytes) {
141: bytes[0] = (byte) ((s & 0xff00) >> 8);
142: bytes[1] = (byte) (s & 0x00ff);
143: }
144:
145: protected static String toHex(byte[] bytes, int byteCnt) {
146: StringBuffer sb = new StringBuffer();
147: for (int i = 0; i < byteCnt; i++) {
148: byte b = bytes[i];
149: byte b1 = (byte) ((b & 0xf0) >> 4);
150: byte b2 = (byte) (b & 0x0f);
151: sb.append(HEXCHARS[b1]).append(HEXCHARS[b2]);
152: }
153: return sb.toString();
154: }
155: }
|