org.apache.lucene.gdata.storage.IDGenerator.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.lucene.gdata.storage.IDGenerator.java

Source

/** 
 * 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();
    }
}