Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
SessionIdGenerator |
|
| 1.5;1.5 |
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 | 105 | private int counter = 0; |
74 | /** The last time. */ | |
75 | 105 | private long lastTimeValue = 0; |
76 | /** The randmonizer. */ | |
77 | 1 | private static Random randomizer = new Random(); |
78 | ||
79 | /** | |
80 | * Constructor. | |
81 | */ | |
82 | public SessionIdGenerator() { | |
83 | 105 | super(); |
84 | 105 | } |
85 | ||
86 | public long maxLength() { | |
87 | 4 | return RANDOM_LENGTH + TIME_LENGTH |
88 | + AbstractStringIdentifierGenerator.MAX_INT_ALPHANUMERIC_VALUE_LENGTH; | |
89 | } | |
90 | ||
91 | public long minLength() { | |
92 | 4 | 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 | 136397 | long currentRandom = randomizer.nextLong(); |
111 | 136397 | if (currentRandom < 0) { |
112 | 68202 | 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 | 136397 | currentRandom %= MAX_RANDOM_LEN; |
118 | 136397 | currentRandom += MAX_RANDOM_LEN; |
119 | ||
120 | 136397 | long currentTimeValue = 0; |
121 | 136397 | int currentCount = 0; |
122 | ||
123 | 136397 | synchronized (this) { |
124 | // Time | |
125 | //-------------- | |
126 | 136397 | 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 | 136397 | currentTimeValue %= MAX_TIME_SECTION_LEN; |
132 | 136397 | 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 | 136397 | if (lastTimeValue != currentTimeValue) { |
142 | 102 | lastTimeValue = currentTimeValue; |
143 | 102 | counter = 0; |
144 | } | |
145 | 136397 | currentCount = counter++; |
146 | 136397 | } |
147 | ||
148 | // build string | |
149 | //-------------- | |
150 | 136397 | StringBuffer id = new StringBuffer |
151 | (AbstractStringIdentifierGenerator.DEFAULT_ALPHANUMERIC_IDENTIFIER_SIZE); | |
152 | 136397 | id.append(Long.toString(currentRandom, |
153 | AbstractStringIdentifierGenerator.ALPHA_NUMERIC_CHARSET_SIZE).substring(1)); // 6 chars | |
154 | 136397 | id.append(Long.toString(currentTimeValue, |
155 | AbstractStringIdentifierGenerator.ALPHA_NUMERIC_CHARSET_SIZE).substring(1)); // 3 chars | |
156 | 136397 | id.append(Long.toString(currentCount, |
157 | AbstractStringIdentifierGenerator.ALPHA_NUMERIC_CHARSET_SIZE)); // 1+ chars | |
158 | 136397 | return id.toString(); |
159 | } | |
160 | } |