1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.apache.commons.id.random; 19 20 import org.apache.commons.id.AbstractStringIdentifierGenerator; 21 22 import java.io.Serializable; 23 import java.util.Random; 24 25 /** 26 * <code>SessionIdGenerator</code> is an identifier generator 27 * that generates an alphanumeric 10+ character identifier. 28 * 29 * <p>The exact length depends on the number of ids requested per time 30 * period. Multiple instances of the class generate still unique ids.</p> 31 * 32 * <p>Originally designed for JServ sessions. Uses synchronized count and 33 * time to ensure uniqueness. Not guaranteed unique across JVMs, but 34 * fairly safe nonetheless.</p> 35 36 * @author Commons-Id team 37 * @version $Id: SessionIdGenerator.java 480488 2006-11-29 08:57:26Z bayard $ 38 */ 39 public class SessionIdGenerator extends AbstractStringIdentifierGenerator implements Serializable { 40 41 /** 42 * <code>serialVersionUID</code> is the serializable UID for the binary version of the class. 43 */ 44 private static final long serialVersionUID = 20060118L; 45 /** 46 * We want to have a random string with a length of 6 characters. 47 * Since we encode it base-36, we modulo the random number with 48 * this value. 49 */ 50 private static final long MAX_RANDOM_LEN = 2176782336L; // 36 ** 6 51 /** 52 * <p>The identifier must be unique within the typical lifespan of a 53 * session; the value can roll over after that.</p>3 characters: 54 * (this means a roll over after over a day, which is much larger 55 * than a typical lifespan). 56 */ 57 private static final long MAX_TIME_SECTION_LEN = 46656L; // 36 ** 3 58 /** 59 * Milliseconds between different tics. The 3-character time 60 * string has a new value every 2 seconds. 61 */ 62 private static final long TIC_DIFFERENCE = 2000; 63 /** 64 * Length of random segment 65 */ 66 private static final int RANDOM_LENGTH = 6; 67 /** 68 * Length of time segment 69 */ 70 private static final int TIME_LENGTH = 3; 71 72 /** The incrementing counter. */ 73 private int counter = 0; 74 /** The last time. */ 75 private long lastTimeValue = 0; 76 /** The randmonizer. */ 77 private static Random randomizer = new Random(); 78 79 /** 80 * Constructor. 81 */ 82 public SessionIdGenerator() { 83 super(); 84 } 85 86 public long maxLength() { 87 return RANDOM_LENGTH + TIME_LENGTH 88 + AbstractStringIdentifierGenerator.MAX_INT_ALPHANUMERIC_VALUE_LENGTH; 89 } 90 91 public long minLength() { 92 return RANDOM_LENGTH + TIME_LENGTH + 1; 93 } 94 95 /** 96 * Gets the next new identifier. 97 * 98 * <p>Only guaranteed unique within this JVM, but fairly safe 99 * for cross JVM usage as well.</p> 100 * 101 * <p>Format of identifier is 102 * <code>[6 chars random][3 chars time][1+ chars count]</code></p> 103 * 104 * @return the next 10 char String identifier 105 */ 106 public String nextStringIdentifier() { 107 108 // Random value 109 //-------------- 110 long currentRandom = randomizer.nextLong(); 111 if (currentRandom < 0) { 112 currentRandom = -currentRandom; 113 } 114 // force value into 6 char range, and add to pad with zeros 115 // this gives a length of 7, when converted to base 36, and 116 // the first character (always 1 from the add) is dropped 117 currentRandom %= MAX_RANDOM_LEN; 118 currentRandom += MAX_RANDOM_LEN; 119 120 long currentTimeValue = 0; 121 int currentCount = 0; 122 123 synchronized (this) { 124 // Time 125 //-------------- 126 currentTimeValue = (System.currentTimeMillis() / TIC_DIFFERENCE); 127 128 // force value into 3 char range, and add to pad with zeros 129 // this gives a length of 4, when converted to base 36, and 130 // the first character (always 1 from the add) is dropped 131 currentTimeValue %= MAX_TIME_SECTION_LEN; 132 currentTimeValue += MAX_TIME_SECTION_LEN; 133 134 // Count 135 //-------------- 136 // Make the string unique by appending the count since last 137 // time flip. 138 139 // Count sessions only within tics (so the 'real' counter 140 // isn't exposed to the public). 141 if (lastTimeValue != currentTimeValue) { 142 lastTimeValue = currentTimeValue; 143 counter = 0; 144 } 145 currentCount = counter++; 146 } 147 148 // build string 149 //-------------- 150 StringBuffer id = new StringBuffer 151 (AbstractStringIdentifierGenerator.DEFAULT_ALPHANUMERIC_IDENTIFIER_SIZE); 152 id.append(Long.toString(currentRandom, 153 AbstractStringIdentifierGenerator.ALPHA_NUMERIC_CHARSET_SIZE).substring(1)); // 6 chars 154 id.append(Long.toString(currentTimeValue, 155 AbstractStringIdentifierGenerator.ALPHA_NUMERIC_CHARSET_SIZE).substring(1)); // 3 chars 156 id.append(Long.toString(currentCount, 157 AbstractStringIdentifierGenerator.ALPHA_NUMERIC_CHARSET_SIZE)); // 1+ chars 158 return id.toString(); 159 } 160 }