Coverage Report - org.apache.commons.id.random.SessionIdGenerator
 
Classes in this File Line Coverage Branch Coverage Complexity
SessionIdGenerator
100%
28/28
100%
4/4
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  
 }