View Javadoc
1   package org.apache.turbine.util;
2   
3   
4   /*
5    * Licensed to the Apache Software Foundation (ASF) under one
6    * or more contributor license agreements.  See the NOTICE file
7    * distributed with this work for additional information
8    * regarding copyright ownership.  The ASF licenses this file
9    * to you under the Apache License, Version 2.0 (the
10   * "License"); you may not use this file except in compliance
11   * with the License.  You may obtain a copy of the License at
12   *
13   *   http://www.apache.org/licenses/LICENSE-2.0
14   *
15   * Unless required by applicable law or agreed to in writing,
16   * software distributed under the License is distributed on an
17   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18   * KIND, either express or implied.  See the License for the
19   * specific language governing permissions and limitations
20   * under the License.
21   */
22  
23  
24  import java.util.Random;
25  
26  /**
27   * This class generates a unique 10+ character id.  This is good for
28   * authenticating users or tracking users around.
29   *
30   * <p>This code was borrowed from Apache
31   * JServ.JServServletManager.java.  It is what Apache JServ uses to
32   * generate session ids for users.  Unfortunately, it was not included
33   * in Apache JServ as a class, so I had to create one here in order to
34   * use it.
35   *
36   * @author <a href="mailto:jon@clearink.com">Jon S. Stevens</a>
37   * @author <a href="mailto:neeme@one.lv">Neeme Praks</a>
38   * @version $Id: GenerateUniqueId.java 1709648 2015-10-20 17:08:10Z tv $
39   */
40  public class GenerateUniqueId
41  {
42      /*
43       * Create a suitable string for session identification.  Use
44       * synchronized count and time to ensure uniqueness.  Use random
45       * string to ensure the timestamp cannot be guessed by programmed
46       * attack.
47       *
48       * Format of id is <6 chars random><3 chars time><1+ char count>
49       */
50      static private int session_count = 0;
51      static private long lastTimeVal = 0;
52      static private Random randomSource = new java.util.Random();
53  
54      // MAX_RADIX is 36
55  
56      /**
57       * We want to have a random string with a length of 6 characters.
58       * Since we encode it BASE 36, we've to modulo it with the
59       * following value:
60       */
61      public final static long maxRandomLen = 2176782336L; // 36 ** 6
62  
63      /**
64       * The session identifier must be unique within the typical
65       * lifespan of a Session; the value can roll over after that.  3
66       * characters: (this means a roll over after over a day, which is
67       * much larger than a typical lifespan)
68       */
69      public final static long maxSessionLifespanTics = 46656; // 36 ** 3
70  
71      /**
72       * Millisecons between different tics.  So this means that the
73       * 3-character time string has a new value every 2 seconds:
74       */
75      public final static long ticDifference = 2000;
76  
77      /**
78       * Get the unique id.
79       *
80       * <p>NOTE: This must work together with
81       * get_jserv_session_balance() in jserv_balance.c
82       *
83       * @return A String with the new unique id.
84       */
85      static synchronized public String getIdentifier()
86      {
87          StringBuilder sessionId = new StringBuilder();
88  
89          // Random value.
90          long n = randomSource.nextLong();
91          if (n < 0)
92          {
93              n = -n;
94          }
95          n %= maxRandomLen;
96  
97          // Add maxLen to pad the leading characters with '0'; remove
98          // first digit with substring.
99          n += maxRandomLen;
100         sessionId.append(Long.toString(n, Character.MAX_RADIX)
101                 .substring(1));
102 
103         long timeVal = (System.currentTimeMillis() / ticDifference);
104 
105         // Cut.
106         timeVal %= maxSessionLifespanTics;
107 
108         // Padding, see above.
109         timeVal += maxSessionLifespanTics;
110 
111         sessionId.append(Long.toString(timeVal, Character.MAX_RADIX)
112                 .substring(1));
113 
114         /*
115          * Make the string unique: append the session count since last
116          * time flip.
117          */
118 
119         // Count sessions only within tics.  So the 'real' session
120         // count isn't exposed to the public.
121         if (lastTimeVal != timeVal)
122         {
123             lastTimeVal = timeVal;
124             session_count = 0;
125         }
126         sessionId.append(Long.toString(++session_count,
127                 Character.MAX_RADIX));
128 
129         return sessionId.toString();
130     }
131 
132     /**
133      * Get the unique id.
134      *
135      * @param jsIdent A String.
136      * @return A String with the new unique id.
137      */
138     synchronized public String getIdentifier(String jsIdent)
139     {
140         if (jsIdent != null && jsIdent.length() > 0)
141         {
142             return getIdentifier() + "." + jsIdent;
143         }
144         return getIdentifier();
145     }
146 
147     /**
148      * Simple test of the functionality.
149      *
150      * @param args A String[] with the command line arguments.
151      */
152     public static void main(String[] args)
153     {
154         System.out.println(GenerateUniqueId.getIdentifier());
155         System.out.println(GenerateUniqueId.getIdentifier());
156         System.out.println(GenerateUniqueId.getIdentifier());
157         System.out.println(GenerateUniqueId.getIdentifier());
158     }
159 }