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