All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.activemq.artemis.utils.UUIDTimer Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.activemq.artemis.utils;

import java.util.Random;

/**
 * UUIDTimer produces the time stamps required for time-based UUIDs. It works as
 * outlined in the UUID specification, with following implementation:
 * 
    *
  • Java classes can only product time stamps with maximum resolution of one * millisecond (at least before JDK 1.5). To compensate, an additional counter * is used, so that more than one UUID can be generated between java clock * updates. Counter may be used to generate up to 10000 UUIDs for each distinct * java clock value. *
  • Due to even lower clock resolution on some platforms (older Windows * versions use 55 msec resolution), timestamp value can also advanced ahead of * physical value within limits (by default, up 100 millisecond ahead of * reported), iff necessary (ie. 10000 instances created before clock time * advances). *
  • As an additional precaution, counter is initialized not to 0 but to a * random 8-bit number, and each time clock changes, lowest 8-bits of counter * are preserved. The purpose it to make likelihood of multi-JVM multi-instance * generators to collide, without significantly reducing max. UUID generation * speed. Note though that using more than one generator (from separate JVMs) is * strongly discouraged, so hopefully this enhancement isn't needed. This 8-bit * offset has to be reduced from total max. UUID count to preserve ordering * property of UUIDs (ie. one can see which UUID was generated first for given * UUID generator); the resulting 9500 UUIDs isn't much different from the * optimal choice. *
  • Finally, as of version 2.0 and onwards, optional external timestamp * synchronization can be done. This is done similar to the way UUID * specification suggests; except that since there is no way to lock the whole * system, file-based locking is used. This works between multiple JVMs and Jug * instances. *
*

* Some additional assumptions about calculating the timestamp: *

    *
  • System.currentTimeMillis() is assumed to give time offset in UTC, or at * least close enough thing to get correct timestamps. The alternate route would * have to go through calendar object, use TimeZone offset to get to UTC, and * then modify. Using currentTimeMillis should be much faster to allow rapid * UUID creation. *
  • Similarly, the constant used for time offset between 1.1.1970 and start * of Gregorian calendar is assumed to be correct (which seems to be the case * when testing with Java calendars). *
*

* Note about synchronization: this class is assumed to always be called from a * synchronized context (caller locks on either this object, or a similar timer * lock), and so has no method synchronization. */ public class UUIDTimer { /** * Since System.longTimeMillis() returns time from january 1st 1970, and * UUIDs need time from the beginning of gregorian calendar (15-oct-1582), * need to apply the offset: */ private static final long kClockOffset = 0x01b21dd213814000L; /** * Also, instead of getting time in units of 100nsecs, we get something with * max resolution of 1 msec... and need the multiplier as well */ private static final long kClockMultiplier = 10000; private static final long kClockMultiplierL = 10000L; /** * Let's allow "virtual" system time to advance at most 100 milliseconds * beyond actual physical system time, before adding delays. */ private static final long kMaxClockAdvance = 100L; // // // Configuration private final Random mRnd; // // // Clock state: /** * Additional state information used to protect against anomalous cases * (clock time going backwards, node id getting mixed up). Third byte is * actually used for seeding counter on counter overflow. */ private final byte[] mClockSequence = new byte[3]; /** * Last physical timestamp value System.currentTimeMillis() * returned: used to catch (and report) cases where system clock goes * backwards. Is also used to limit "drifting", that is, amount timestamps * used can differ from the system time value. This value is not guaranteed * to be monotonically increasing. */ private long mLastSystemTimestamp = 0L; /** * Timestamp value last used for generating a UUID (along with * {@link #mClockCounter}. Usually the same as {@link #mLastSystemTimestamp}, * but not always (system clock moved backwards). Note that this value is * guaranteed to be monotonically increasing; that is, at given absolute time * points t1 and t2 (where t2 is after t1), t1 <= t2 will always hold true. */ private long mLastUsedTimestamp = 0L; /** * Counter used to compensate inadequate resolution of JDK system timer. */ private int mClockCounter = 0; UUIDTimer(final Random rnd) { mRnd = rnd; initCounters(rnd); mLastSystemTimestamp = 0L; // This may get overwritten by the synchronizer mLastUsedTimestamp = 0L; } private void initCounters(final Random rnd) { /* * Let's generate the clock sequence field now; as with counter, this * reduces likelihood of collisions (as explained in UUID specs) */ rnd.nextBytes(mClockSequence); /* * Ok, let's also initialize the counter... Counter is used to make it * slightly less likely that two instances of UUIDGenerator (from separate * JVMs as no more than one can be created in one JVM) would produce * colliding time-based UUIDs. The practice of using multiple generators, * is strongly discouraged, of course, but just in case... */ mClockCounter = mClockSequence[2] & 0xFF; } public void getTimestamp(final byte[] uuidData) { // First the clock sequence: uuidData[UUID.INDEX_CLOCK_SEQUENCE] = mClockSequence[0]; uuidData[UUID.INDEX_CLOCK_SEQUENCE + 1] = mClockSequence[1]; long systime = System.currentTimeMillis(); /* * Let's first verify that the system time is not going backwards; * independent of whether we can use it: */ if (systime < mLastSystemTimestamp) { // Logger.logWarning("System time going backwards! (got value // "+systime+", last "+mLastSystemTimestamp); // Let's write it down, still mLastSystemTimestamp = systime; } /* * But even without it going backwards, it may be less than the last one * used (when generating UUIDs fast with coarse clock resolution; or if * clock has gone backwards over reboot etc). */ if (systime <= mLastUsedTimestamp) { /* * Can we just use the last time stamp (ok if the counter hasn't hit * max yet) */ if (mClockCounter < UUIDTimer.kClockMultiplier) { // yup, still have room systime = mLastUsedTimestamp; } else { // nope, have to roll over to next value and maybe wait long actDiff = mLastUsedTimestamp - systime; long origTime = systime; systime = mLastUsedTimestamp + 1L; // Logger.logWarning("Timestamp over-run: need to reinitialize // random sequence"); /* * Clock counter is now at exactly the multiplier; no use just * anding its value. So, we better get some random numbers * instead... */ initCounters(mRnd); /* * But do we also need to slow down? (to try to keep virtual time * close to physical time; ie. either catch up when system clock has * been moved backwards, or when coarse clock resolution has forced * us to advance virtual timer too far) */ if (actDiff >= UUIDTimer.kMaxClockAdvance) { UUIDTimer.slowDown(origTime, actDiff); } } } else { /* * Clock has advanced normally; just need to make sure counter is reset * to a low value (need not be 0; good to leave a small residual to * further decrease collisions) */ mClockCounter &= 0xFF; } mLastUsedTimestamp = systime; /* * Now, let's translate the timestamp to one UUID needs, 100ns unit offset * from the beginning of Gregorian calendar... */ systime *= UUIDTimer.kClockMultiplierL; systime += UUIDTimer.kClockOffset; // Plus add the clock counter: systime += mClockCounter; // and then increase ++mClockCounter; /* * Time fields are nicely split across the UUID, so can't just linearly * dump the stamp: */ int clockHi = (int) (systime >>> 32); int clockLo = (int) systime; uuidData[UUID.INDEX_CLOCK_HI] = (byte) (clockHi >>> 24); uuidData[UUID.INDEX_CLOCK_HI + 1] = (byte) (clockHi >>> 16); uuidData[UUID.INDEX_CLOCK_MID] = (byte) (clockHi >>> 8); uuidData[UUID.INDEX_CLOCK_MID + 1] = (byte) clockHi; uuidData[UUID.INDEX_CLOCK_LO] = (byte) (clockLo >>> 24); uuidData[UUID.INDEX_CLOCK_LO + 1] = (byte) (clockLo >>> 16); uuidData[UUID.INDEX_CLOCK_LO + 2] = (byte) (clockLo >>> 8); uuidData[UUID.INDEX_CLOCK_LO + 3] = (byte) clockLo; } private static final int MAX_WAIT_COUNT = 50; /** * Simple utility method to use to wait for couple of milliseconds, to let * system clock hopefully advance closer to the virtual timestamps used. * Delay is kept to just a millisecond or two, to prevent excessive blocking; * but that should be enough to eventually synchronize physical clock with * virtual clock values used for UUIDs. */ private static void slowDown(final long startTime, final long actDiff) { /* * First, let's determine how long we'd like to wait. This is based on how * far ahead are we as of now. */ long ratio = actDiff / UUIDTimer.kMaxClockAdvance; long delay; if (ratio < 2L) { // 200 msecs or less delay = 1L; } else if (ratio < 10L) { // 1 second or less delay = 2L; } else if (ratio < 600L) { // 1 minute or less delay = 3L; } else { delay = 5L; } // Logger.logWarning("Need to wait for "+delay+" milliseconds; virtual // clock advanced too far in the future"); long waitUntil = startTime + delay; int counter = 0; do { try { Thread.sleep(delay); } catch (InterruptedException ie) { } delay = 1L; /* * This is just a sanity check: don't want an "infinite" loop if clock * happened to be moved backwards by, say, an hour... */ if (++counter > UUIDTimer.MAX_WAIT_COUNT) { break; } } while (System.currentTimeMillis() < waitUntil); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy