FileDocCategorySizeDatePackage
IDGenerator.javaAPI DocApache Lucene 2.1.05852Wed Feb 14 10:46:04 GMT 2007org.apache.lucene.gdata.storage

IDGenerator.java

/** 
 * Copyright 2004 The Apache Software Foundation 
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0 
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License. 
 */ 
 
package org.apache.lucene.gdata.storage; 
 
import java.security.MessageDigest; 
import java.security.NoSuchAlgorithmException; 
import java.security.SecureRandom; 
import java.util.concurrent.ArrayBlockingQueue; 
import java.util.concurrent.BlockingQueue; 
import java.util.concurrent.atomic.AtomicBoolean;
 
import org.apache.commons.logging.Log; 
import org.apache.commons.logging.LogFactory; 
 
/** 
 * This is the main entry ID generator to generate unique ids for each entry. 
 * The Generator uses {@link java.security.SecureRandom} Numbers and the 
 * {@link java.lang.System#currentTimeMillis()} to create a semi-unique sting; 
 * The string will be digested by a {@link java.security.MessageDigest} which 
 * returns a byte array. The generator encodes the byte array as a hex string. 
 * <p> 
 * The generated Id's will cached in a 
 * {@link java.util.concurrent.BlockingQueue} and reproduced if an id has been 
 * removed. 
 * </p> 
 *  
 * @author Simon Willnauer 
 *  
 */ 
public class IDGenerator { 
    final AtomicBoolean stopped = new AtomicBoolean(false);
    
    private final SecureRandom secureRandom; 
 
    private final MessageDigest mdigest; 
 
    private final BlockingQueue<String> blockingQueue; 
 
    private Thread runner; 
 
    private static final int DEFAULT_CAPACITY = 10; 
 
    protected static final Log LOGGER = LogFactory.getLog(IDGenerator.class);

    private static final String RUNNER_THREAD_NAME = "GDATA-ID Generator"; 
 
    /** 
     * Constructs a new ID generator. with a fixed capacity of prebuild ids. The 
     * default capacity is 10. Every given parameter less than 10 will be 
     * ignored. 
     *  
     * @param capacity - 
     *            capacity of the prebuild id queue 
     * @throws NoSuchAlgorithmException - 
     *             if the algorithm does not exist 
     */ 
    public IDGenerator(int capacity) throws NoSuchAlgorithmException { 
 
        this.secureRandom = SecureRandom.getInstance("SHA1PRNG"); 
        this.mdigest = MessageDigest.getInstance("SHA-1"); 
        this.blockingQueue = new ArrayBlockingQueue<String>( 
                (capacity < DEFAULT_CAPACITY ? DEFAULT_CAPACITY : capacity), 
                false); 
        startIDProducer(); 
 
    } 
 
    /** 
     * This method takes a gnerated id from the IDProducer queue and retruns it. 
     * If no ID is available this method will wait until an ID is produced. This 
     * implementation is thread-safe. 
     *  
     * @return a UID 
     * @throws InterruptedException - 
     *             if interrupted while waiting 
     */ 
    public String getUID() throws InterruptedException { 
        return this.blockingQueue.take(); 
    } 
 
    private void startIDProducer() { 
        if (this.runner == null) { 
            UIDProducer producer = new UIDProducer(this.blockingQueue, 
                    this.secureRandom, this.mdigest); 
            this.runner = new Thread(producer);
            this.runner.setDaemon(true);
            this.runner.setName(RUNNER_THREAD_NAME);
            this.runner.start(); 
        } 
    } 
 
    /** 
     * @return the current size of the queue 
     */ 
    public int getQueueSize() { 
        return this.blockingQueue.size(); 
    } 
 
    /** 
     * Stops the id-producer 
     */ 
    public void stopIDGenerator() {
        this.stopped.set(true);
        this.runner.interrupt(); 
        
    } 
 
    private class UIDProducer implements Runnable { 
        SecureRandom random; 
 
        BlockingQueue<String> queue; 
 
        MessageDigest digest; 
 
        UIDProducer(BlockingQueue<String> queue, SecureRandom random, 
                MessageDigest digest) { 
            this.queue = queue; 
            this.random = random; 
            this.digest = digest; 
 
        } 
 
        /** 
         * @see java.lang.Runnable#run() 
         */ 
        public void run() { 
 
            while (!IDGenerator.this.stopped.get()) { 
                try { 
                    this.queue.put(produce()); 
                } catch (InterruptedException e) {
                    LOGGER 
                            .warn("UIDProducer has been interrupted -- runner is going down"); 
                    return; 
                } 
            } 
 
        } 
 
        private String produce() { 
            String randomNumber = Integer.toString(this.random.nextInt()); 
            byte[] byteResult = this.digest.digest(randomNumber.getBytes()); 
            return hexEncode(byteResult); 
        } 
 
    } 
 
    /** 
     * Encodes a given byte array into a hex string. 
     *  
     * @param input - 
     *            the byte array to encode 
     * @return hex string representation of the given byte array 
     */ 
    static String hexEncode(byte[] input) { 
        StringBuffer result = new StringBuffer(); 
        char[] digits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 
                'a', 'b', 'c', 'd', 'e', 'f' }; 
        for (int idx = 0; idx < input.length; ++idx) { 
            byte b = input[idx]; 
            result.append(digits[(b & 0xf0) >> 4]); 
            result.append(digits[b & 0x0f]); 
        } 
        return result.toString(); 
    } 
}