/* * JBoss, Home of Professional Open Source * Copyright 2005-2008, Red Hat Middleware LLC, and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.messaging.util; import java.util.concurrent.atomic.AtomicLong; /** * A TimeAndCounterIDGenerator * <p> * This IDGenerator doesn't support more than 16777215 IDs per 16 millisecond. It would throw an exception if this happens. * </p> * * @author <a href="mailto:clebert.suconic@jboss.org">Clebert Suconic</a> * @author <a href="mailto:tim.fox@jboss.com">Tim Fox</a> Created Sep 24, 2008 11:54:10 AM */ public class TimeAndCounterIDGenerator implements IDGenerator { // Constants ---------------------------------------------------- /** * Bits to move the date accordingly to MASK_TIME */ private static final int BITS_TO_MOVE = 20; public static final long MASK_TIME = 0x7fffffffff0l; //44 bits of time and 20 bits of counter public static final long ID_MASK = 0xffffffl; private static final long TIME_ID_MASK = 0x7fffffffff000000l; // Attributes ---------------------------------------------------- private final AtomicLong counter = new AtomicLong(0); private volatile boolean wrapped = false; private volatile long tmMark; // Static -------------------------------------------------------- // Constructors -------------------------------------------------- public TimeAndCounterIDGenerator() { refresh(); } // Public -------------------------------------------------------- // Public -------------------------------------------------------- public long generateID() { long idReturn = counter.incrementAndGet(); if ((idReturn & ID_MASK) == 0) { final long timePortion = idReturn & TIME_ID_MASK; // Wrapping ID logic if (timePortion >= newTM()) { // Unlikely to happen wrapped = true; } else { // Else.. no worry... we will just accept the new time portion being added // This time-mark would have been generated some time ago, so this is ok. // tmMark is just a cache to validate the MaxIDs, so there is no need to make it atomic (synchronized) tmMark = timePortion; } } if (wrapped) { // This will only happen if a computer can generate more than ID_MASK ids (16 million IDs per 16 // milliseconds) // If this wrapping code starts to happen, it needs revision throw new IllegalStateException("The IDGenerator is being overlaped, and it needs revision as the system generated more than " + ID_MASK + " ids per 16 milliseconds which exceeded the IDgenerator limit"); } return idReturn; } public long getCurrentID() { return counter.get(); } // for use in testcases public long getInternalTimeMark() { return tmMark; } // for use in testcases public void setInternalID(final long id) { counter.set(tmMark | id); } // for use in testcases public void setInternalDate(final long date) { tmMark = (date & MASK_TIME) << BITS_TO_MOVE; counter.set(tmMark); } public synchronized void refresh() { long oldTm = tmMark; long newTm = newTM(); while (newTm <= oldTm) { newTm = newTM(); } tmMark = newTm; counter.set(tmMark); } @Override public String toString() { long currentCounter = counter.get(); return "SequenceGenerator(tmMark=" + hex(tmMark) + ", CurrentCounter = " + currentCounter + ", HexCurrentCounter = " + hex(currentCounter) + ")"; } // Package protected --------------------------------------------- // Protected ----------------------------------------------------- // Private ------------------------------------------------------- private long newTM() { return (System.currentTimeMillis() & MASK_TIME) << BITS_TO_MOVE; } private String hex(final long x) { return String.format("%1$X", x); } }