Home | History | Annotate | Download | only in jbosh
      1 /*
      2  * Copyright 2009 Mike Cumings
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *   http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.kenai.jbosh;
     18 
     19 import java.security.SecureRandom;
     20 import java.util.concurrent.atomic.AtomicLong;
     21 import java.util.concurrent.locks.Lock;
     22 import java.util.concurrent.locks.ReentrantLock;
     23 
     24 /**
     25  * Request ID sequence generator.  This generator generates a random first
     26  * RID and then manages the sequence from there on out.
     27  */
     28 final class RequestIDSequence {
     29 
     30     /**
     31      * Maximum number of bits available for representing request IDs, according
     32      * to the XEP-0124 spec.s
     33      */
     34     private static final int MAX_BITS = 53;
     35 
     36     /**
     37      * Bits devoted to incremented values.
     38      */
     39     private static final int INCREMENT_BITS = 32;
     40 
     41     /**
     42      * Minimum number of times the initial RID can be incremented before
     43      * exceeding the maximum.
     44      */
     45     private static final long MIN_INCREMENTS = 1L << INCREMENT_BITS;
     46 
     47     /**
     48      * Max initial value.
     49      */
     50     private static final long MAX_INITIAL = (1L << MAX_BITS) - MIN_INCREMENTS;
     51 
     52     /**
     53      * Max bits mask.
     54      */
     55     private static final long MASK = ~(Long.MAX_VALUE << MAX_BITS);
     56 
     57     /**
     58      * Random number generator.
     59      */
     60     private static final SecureRandom RAND = new SecureRandom();
     61 
     62     /**
     63      * Internal lock.
     64      */
     65     private static final Lock LOCK = new ReentrantLock();
     66 
     67     /**
     68      * The last reqest ID used, or &lt;= 0 if a new request ID needs to be
     69      * generated.
     70      */
     71     private AtomicLong nextRequestID = new AtomicLong();
     72 
     73     ///////////////////////////////////////////////////////////////////////////
     74     // Constructors:
     75 
     76     /**
     77      * Prevent direct construction.
     78      */
     79     RequestIDSequence() {
     80         nextRequestID = new AtomicLong(generateInitialValue());
     81     }
     82 
     83     ///////////////////////////////////////////////////////////////////////////
     84     // Public methods:
     85 
     86     /**
     87      * Calculates the next request ID value to use.  This number must be
     88      * initialized such that it is unlikely to ever exceed 2 ^ 53, according
     89      * to XEP-0124.
     90      *
     91      * @return next request ID value
     92      */
     93     public long getNextRID() {
     94         return nextRequestID.getAndIncrement();
     95     }
     96 
     97     ///////////////////////////////////////////////////////////////////////////
     98     // Private methods:
     99 
    100     /**
    101      * Generates an initial RID value by generating numbers until a number is
    102      * found which is smaller than the maximum allowed value and greater
    103      * than zero.
    104      *
    105      * @return random initial value
    106      */
    107     private long generateInitialValue() {
    108         long result;
    109         LOCK.lock();
    110         try {
    111             do {
    112                 result = RAND.nextLong() & MASK;
    113             } while (result > MAX_INITIAL);
    114         } finally {
    115             LOCK.unlock();
    116         }
    117         return result;
    118     }
    119 
    120 }
    121