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

org.openmdx.kernel.id.spi.TimeBasedIdGenerator Maven / Gradle / Ivy

There is a newer version: 2.18.10
Show newest version
/*
 * ====================================================================
 * Project:     openMDX, http://www.openmdx.org/
 * Description: Time Based Id Provider using Random based Node
 * Owner:       OMEX AG, Switzerland, http://www.omex.ch
 * ====================================================================
 *
 * This software is published under the BSD license as listed below.
 * 
 * Copyright (c) 2004-2013, OMEX AG, Switzerland
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions 
 * are met:
 * 
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * 
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in
 *   the documentation and/or other materials provided with the
 *   distribution.
 * 
 * * Neither the name of the openMDX team nor the names of its
 *   contributors may be used to endorse or promote products derived
 *   from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * 
 * ------------------
 * 
 * This product includes software developed by the Apache Software
 * Foundation (http://www.apache.org/).
 */
package org.openmdx.kernel.id.spi;

import java.security.SecureRandom;
import java.util.Date;
import java.util.logging.Level;

import org.openmdx.kernel.exception.BasicException;
import org.openmdx.kernel.log.SysLog;
import org.w3c.format.DateTimeFormat;

/**
 * Time Based Id Provider 
 */
public abstract class TimeBasedIdGenerator extends TimeBasedIdBuilder {

    /**
     * The time frame reserved for this generator instance
     */
    private TimeFrame timeFrame;
    
    /**
     * Since System.currentTimeMillis() 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 OFFSET = 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 FRAME_SIZE = 10000L;
    
    /**
     * Remember the last used system timestamp to avoid returning the
     * same time frame twice.
     */
    private static volatile long lastReservation = -1L;
    
    /**
     * The random number generator used by this class to create clock sequences.
     */
    private static final SecureRandom random = new SecureRandom();
    
    /**
     * The clock sequence
     */
    private static volatile int clockSequence = createClockSequence();
    
    
    /**
     * The 14 bit clock sequence value is used to set the clock sequence field 
     * of this UUID. The clock sequence field is used to guarantee temporal 
     * uniqueness in a time-based UUID.
     * 

* The clock sequence has to be provided by a subclass. * * @return this UUID prvider's clock sequence. */ @Override protected int getClockSequence( ){ return TimeBasedIdGenerator.clockSequence; } /** * The timestamp is measured in 100-nanosecond units since midnight, * October 15, 1582 UTC. *

* This method may be overridden by a subclass. *

* The 60 bit timestamp value is used to set the the time_low, time_mid, * and time_hi fields of the UUID. * * @return the timestamp for the next UUID */ @Override protected long getTimestamp(){ return getTimeFrame().nextTimeStamp(); } /** * Get a non-exhausted time frame */ private TimeFrame getTimeFrame( ){ if(this.timeFrame == null || this.timeFrame.isExhausted()) try { this.timeFrame = newTimeFrame(); SysLog.detail("New time frame reserved", this.timeFrame); } catch (InterruptedException exception) { throw new RuntimeException( "Time frame acquisition failure", exception ); } return this.timeFrame; } //------------------------------------------------------------------------ // Time Frame Provider //------------------------------------------------------------------------ /** * The random number generator used by this class and its sublcasses * * @return a random number generator */ protected static SecureRandom getRandom( ){ return TimeBasedIdGenerator.random; } /** * Reserves a time frame of a millisecond for a specific UUID generator * instance. * * @return a time frame reserved for a the calling UUID generator instance * * @throws InterruptedException */ private static synchronized TimeFrame newTimeFrame( ) throws InterruptedException{ final long lastReservation = TimeBasedIdGenerator.lastReservation; final long nextReservation = nextReservation(lastReservation); TimeFrame timeFrame = toTimeFrame(lastReservation, nextReservation); TimeBasedIdGenerator.lastReservation = nextReservation; return timeFrame; } /** * Validates the reservation and create the corresponding time frame * * @param lastReservation the last reservation (which maybe belongs to another generator) * @param nextReservation the next reservation for the calling generator * * @return a new time frame for the calling generator */ private static TimeFrame toTimeFrame( final long lastReservation, final long nextReservation ) { final int signum = Long.signum(nextReservation - lastReservation); switch (signum){ case -1: // // Clock has been set back in the meanwhile // changeClockSequence(lastReservation, nextReservation); break; case 0: throw new RuntimeException( BasicException.newStandAloneExceptionStack( BasicException.Code.DEFAULT_DOMAIN, BasicException.Code.ASSERTION_FAILURE, "This time frame is already reserved", new BasicException.Parameter("time-frame", new TimeFrame(nextReservation)) ) ); case 1: // // Normal // break; default: // // Should never happen! // throw new RuntimeException( BasicException.newStandAloneExceptionStack( BasicException.Code.DEFAULT_DOMAIN, BasicException.Code.ASSERTION_FAILURE, "The signum is expectzed to be in the range [-1..1]", new BasicException.Parameter("signum", signum) ) ); } return new TimeFrame(nextReservation); } /** * Change the clock sequence when the clock has been set back * * @param lastReservation the last reservation (which maybe belongs to another generator) * @param nextReservation the next reservation for the calling generator */ private static void changeClockSequence( final long lastReservation, final long nextReservation ) { int clockSequence = (TimeBasedIdGenerator.clockSequence + 1) & 0x3FFF; SysLog.log( Level.WARNING, "Sys|Clock has been set back from {0} to {1}|Clock sequence will be changed from {2} to {3}", DateTimeFormat.EXTENDED_UTC_FORMAT.format(new Date(lastReservation)), DateTimeFormat.EXTENDED_UTC_FORMAT.format(new Date(nextReservation)), Long.valueOf(TimeBasedIdGenerator.clockSequence), Long.valueOf(clockSequence) ); TimeBasedIdGenerator.clockSequence = clockSequence; } /** * Find the next reservation * * @param lastReservation the last reservation * * @return the next reservation * * @throws InterruptedException */ private static long nextReservation( final long lastReservation ) throws InterruptedException { long currentMillisecond = System.currentTimeMillis(); int i = 0; while(currentMillisecond == lastReservation) { i++; Thread.sleep(1L); // wait for 1 ms currentMillisecond = System.currentTimeMillis(); } SysLog.log(Level.FINE, "Sys|Delay for acquiring a new time frame|{0} ms", Integer.valueOf(i)); return currentMillisecond; } /** * Create a random based node */ protected static long createRandomBasedNode(){ long randomBasedNode = (getRandom().nextLong() & 0x0000FEFF0FFFFFFFL) | // Random Address 0x00000100E0000000L; // with the MAC and IP multicast bits set SysLog.info("The time based id generator has a random based node", new HexFormatter(randomBasedNode)); return randomBasedNode; } /** * @return */ private static int createClockSequence() { int clockSequence = getRandom().nextInt(0x4000); SysLog.info("Clock sequence created", new HexFormatter(clockSequence)); return clockSequence; } //------------------------------------------------------------------------ // Class TimeFrame //------------------------------------------------------------------------ /** * Represents a time frame allocated to a single generator */ private static class TimeFrame { /** * Constructor * * @param millisecond milliseconds passed since January 1st 1970 */ TimeFrame( long millisecond ) { this.millisecond = millisecond; this.currentTimeStamp = OFFSET + millisecond * FRAME_SIZE; this.limit = this.currentTimeStamp + FRAME_SIZE; } private final long millisecond; /** * Timestamp in 100-nanosecond units since 1582-10-15T00:00:00Z */ private long currentTimeStamp; private final long limit; boolean isExhausted(){ return this.currentTimeStamp == limit; } /** * Retrieve a 100-nanosecond unit since 1582-10-15T00:00:00Z * * @return the next time stamp */ long nextTimeStamp(){ return this.currentTimeStamp++; } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return DateTimeFormat.EXTENDED_UTC_FORMAT.format(new Date(millisecond)) + "/P0.001S"; } } //------------------------------------------------------------------------ // Class HexFormatter //------------------------------------------------------------------------ /** * Allows loggers to log nodes and clock sequences appropriately */ private static class HexFormatter { HexFormatter(long node) { this.node = node; } private final long node; /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return Long.toHexString(node); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy