001    package org.apache.turbine.util;
002    
003    
004    /*
005     * Licensed to the Apache Software Foundation (ASF) under one
006     * or more contributor license agreements.  See the NOTICE file
007     * distributed with this work for additional information
008     * regarding copyright ownership.  The ASF licenses this file
009     * to you under the Apache License, Version 2.0 (the
010     * "License"); you may not use this file except in compliance
011     * with the License.  You may obtain a copy of the License at
012     *
013     *   http://www.apache.org/licenses/LICENSE-2.0
014     *
015     * Unless required by applicable law or agreed to in writing,
016     * software distributed under the License is distributed on an
017     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
018     * KIND, either express or implied.  See the License for the
019     * specific language governing permissions and limitations
020     * under the License.
021     */
022    
023    
024    import java.util.Random;
025    
026    /**
027     * This class generates a unique 10+ character id.  This is good for
028     * authenticating users or tracking users around.
029     *
030     * <p>This code was borrowed from Apache
031     * JServ.JServServletManager.java.  It is what Apache JServ uses to
032     * generate session ids for users.  Unfortunately, it was not included
033     * in Apache JServ as a class, so I had to create one here in order to
034     * use it.
035     *
036     * @author <a href="mailto:jon@clearink.com">Jon S. Stevens</a>
037     * @author <a href="mailto:neeme@one.lv">Neeme Praks</a>
038     * @version $Id: GenerateUniqueId.java 615328 2008-01-25 20:25:05Z tv $
039     */
040    public class GenerateUniqueId
041    {
042        /*
043         * Create a suitable string for session identification.  Use
044         * synchronized count and time to ensure uniqueness.  Use random
045         * string to ensure the timestamp cannot be guessed by programmed
046         * attack.
047         *
048         * Format of id is <6 chars random><3 chars time><1+ char count>
049         */
050        static private int session_count = 0;
051        static private long lastTimeVal = 0;
052        static private Random randomSource = new java.util.Random();
053    
054        // MAX_RADIX is 36
055    
056        /*
057         * We want to have a random string with a length of 6 characters.
058         * Since we encode it BASE 36, we've to modulo it with the
059         * following value:
060         */
061        public final static long maxRandomLen = 2176782336L; // 36 ** 6
062    
063        /*
064         * The session identifier must be unique within the typical
065         * lifespan of a Session; the value can roll over after that.  3
066         * characters: (this means a roll over after over a day, which is
067         * much larger than a typical lifespan)
068         */
069        public final static long maxSessionLifespanTics = 46656; // 36 ** 3
070    
071        /*
072         * Millisecons between different tics.  So this means that the
073         * 3-character time string has a new value every 2 seconds:
074         */
075        public final static long ticDifference = 2000;
076    
077        /**
078         * Get the unique id.
079         *
080         * <p>NOTE: This must work together with
081         * get_jserv_session_balance() in jserv_balance.c
082         *
083         * @return A String with the new unique id.
084         */
085        static synchronized public String getIdentifier()
086        {
087            StringBuffer sessionId = new StringBuffer();
088    
089            // Random value.
090            long n = randomSource.nextLong();
091            if (n < 0) n = -n;
092            n %= maxRandomLen;
093    
094            // Add maxLen to pad the leading characters with '0'; remove
095            // first digit with substring.
096            n += maxRandomLen;
097            sessionId.append(Long.toString(n, Character.MAX_RADIX)
098                    .substring(1));
099    
100            long timeVal = (System.currentTimeMillis() / ticDifference);
101    
102            // Cut.
103            timeVal %= maxSessionLifespanTics;
104    
105            // Padding, see above.
106            timeVal += maxSessionLifespanTics;
107    
108            sessionId.append(Long.toString(timeVal, Character.MAX_RADIX)
109                    .substring(1));
110    
111            /*
112             * Make the string unique: append the session count since last
113             * time flip.
114             */
115    
116            // Count sessions only within tics.  So the 'real' session
117            // count isn't exposed to the public.
118            if (lastTimeVal != timeVal)
119            {
120                lastTimeVal = timeVal;
121                session_count = 0;
122            }
123            sessionId.append(Long.toString(++session_count,
124                    Character.MAX_RADIX));
125    
126            return sessionId.toString();
127        }
128    
129        /**
130         * Get the unique id.
131         *
132         * @param jsIdent A String.
133         * @return A String with the new unique id.
134         */
135        synchronized public String getIdentifier(String jsIdent)
136        {
137            if (jsIdent != null && jsIdent.length() > 0)
138            {
139                return getIdentifier() + "." + jsIdent;
140            }
141            return getIdentifier();
142        }
143    
144        /**
145         * Simple test of the functionality.
146         *
147         * @param args A String[] with the command line arguments.
148         */
149        public static void main(String[] args)
150        {
151            System.out.println(GenerateUniqueId.getIdentifier());
152            System.out.println(GenerateUniqueId.getIdentifier());
153            System.out.println(GenerateUniqueId.getIdentifier());
154            System.out.println(GenerateUniqueId.getIdentifier());
155        }
156    }