info.archinnov.achilles.internal.utils.UUIDGen Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of achilles-core Show documentation
Show all versions of achilles-core Show documentation
CQL implementation for Achilles using Datastax Java driver
/*
* Copyright (C) 2012-2014 DuyHai DOAN
*
* 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 info.archinnov.achilles.internal.utils;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
/**
* The goods are here: www.ietf.org/rfc/rfc4122.txt.
*/
public class UUIDGen
{
// A grand day! millis at 00:00:00.000 15 Oct 1582.
private static final long START_EPOCH = -12219292800000L;
private static final long clockSeqAndNode = makeClockSeqAndNode();
// placement of this singleton is important. It needs to be instantiated *AFTER* the other statics.
private static final UUIDGen instance = new UUIDGen();
private long lastNanos;
private UUIDGen()
{
// make sure someone didn't whack the clockSeqAndNode by changing the order of instantiation.
if (clockSeqAndNode == 0) throw new RuntimeException("singleton instantiation is misplaced.");
}
/**
* Creates a type 1 UUID (time-based UUID).
*
* @return a UUID instance
*/
public static UUID getTimeUUID()
{
return new UUID(instance.createTimeSafe(), clockSeqAndNode);
}
/**
* @param uuid
* @return microseconds since Unix epoch
*/
public static long microsTimestamp(UUID uuid)
{
final long timestamp = uuid.timestamp();
long remainder = timestamp%1000;
return (timestamp / 10) + START_EPOCH * 1000 + remainder;
}
public static long increasingMicroTimestamp() {
final UUID timeUUID = getTimeUUID();
return microsTimestamp(timeUUID);
}
private static long makeClockSeqAndNode()
{
long clock = new Random(System.currentTimeMillis()).nextLong();
long lsb = 0;
lsb |= 0x8000000000000000L; // variant (2 bits)
lsb |= (clock & 0x0000000000003FFFL) << 48; // clock sequence (14 bits)
lsb |= makeNode(); // 6 bytes
return lsb;
}
// needs to return two different values for the same when.
// we can generate at most 10k UUIDs per ms.
private synchronized long createTimeSafe()
{
long nanosSince = (System.currentTimeMillis() - START_EPOCH) * 10000;
if (nanosSince > lastNanos)
lastNanos = nanosSince;
else
nanosSince = ++lastNanos;
return createTime(nanosSince);
}
private static long createTime(long nanosSince)
{
long msb = 0L;
msb |= (0x00000000ffffffffL & nanosSince) << 32;
msb |= (0x0000ffff00000000L & nanosSince) >>> 16;
msb |= (0xffff000000000000L & nanosSince) >>> 48;
msb |= 0x0000000000001000L; // sets the version to 1.
return msb;
}
private static long makeNode()
{
/*
* We don't have access to the MAC address but need to generate a node part
* that identify this host as uniquely as possible.
* The spec says that one option is to take as many source that identify
* this node as possible and hash them together. That's what we do here by
* gathering all the ip of this host.
* Note that FBUtilities.getBroadcastAddress() should be enough to uniquely
* identify the node *in the cluster* but it triggers DatabaseDescriptor
* instanciation and the UUID generator is used in Stress for instance,
* where we don't want to require the yaml.
*/
Collection localAddresses = getAllLocalAddresses();
if (localAddresses.isEmpty())
throw new RuntimeException("Cannot generate the node component of the UUID because cannot retrieve any IP addresses.");
// ideally, we'd use the MAC address, but java doesn't expose that.
byte[] hash = hash(localAddresses);
long node = 0;
for (int i = 0; i < Math.min(6, hash.length); i++)
node |= (0x00000000000000ff & (long)hash[i]) << (5-i)*8;
assert (0xff00000000000000L & node) == 0;
// Since we don't use the mac address, the spec says that multicast
// bit (least significant bit of the first octet of the node ID) must be 1.
return node | 0x0000010000000000L;
}
private static byte[] hash(Collection data)
{
try
{
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
for(InetAddress addr : data)
messageDigest.update(addr.getAddress());
return messageDigest.digest();
}
catch (NoSuchAlgorithmException nsae)
{
throw new RuntimeException("MD5 digest algorithm is not available", nsae);
}
}
private static Collection getAllLocalAddresses()
{
Set localAddresses = new HashSet();
try
{
Enumeration nets = NetworkInterface.getNetworkInterfaces();
if (nets != null)
{
while (nets.hasMoreElements())
localAddresses.addAll(Collections.list(nets.nextElement().getInetAddresses()));
}
}
catch (SocketException e)
{
throw new AssertionError(e);
}
return localAddresses;
}
}
// for the curious, here is how I generated START_EPOCH
// Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT-0"));
// c.set(Calendar.YEAR, 1582);
// c.set(Calendar.MONTH, Calendar.OCTOBER);
// c.set(Calendar.DAY_OF_MONTH, 15);
// c.set(Calendar.HOUR_OF_DAY, 0);
// c.set(Calendar.MINUTE, 0);
// c.set(Calendar.SECOND, 0);
// c.set(Calendar.MILLISECOND, 0);
// long START_EPOCH = c.getTimeInMillis();
© 2015 - 2025 Weber Informatics LLC | Privacy Policy