View Javadoc

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 }