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

io.stargate.sgv2.graphql.schema.Uuids Maven / Gradle / Ivy

There is a newer version: 2.0.0-ALPHA-17
Show newest version
/*
 * Copyright The Stargate Authors
 *
 * 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 io.stargate.sgv2.graphql.schema;

import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Properties;
import java.util.Random;
import java.util.Set;
import java.util.TimeZone;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Provides utility methods to handle time-based UUIDs.
 *
 * 

Adapted from the Java * driver class of the same name (also under Apache v2 license). */ public class Uuids { private static final long START_EPOCH = makeEpoch(); private static final long CLOCK_SEQ_AND_NODE = makeClockSeqAndNode(); private static final AtomicLong lastTimestamp = new AtomicLong(0L); private Uuids() { // nothing to do } /** * Creates a new time-based (version 1) UUID. * *

UUIDs generated by this method are suitable for use with the {@code timeuuid} Cassandra * type. In particular the generated UUID includes the timestamp of its generation. */ public static UUID timeBased() { return new UUID(makeMsb(getCurrentTimestamp()), CLOCK_SEQ_AND_NODE); } /** * Returns the Unix timestamp contained by the provided time-based UUID. * *

This method is not equivalent to {@link UUID#timestamp()}. More precisely, a version 1 UUID * stores a timestamp that represents the number of 100-nanoseconds intervals since midnight, 15 * October 1582 and that is what {@link UUID#timestamp()} returns. This method however converts * that timestamp to the equivalent Unix timestamp in milliseconds, i.e. a timestamp representing * a number of milliseconds since midnight, January 1, 1970 UTC. In particular, the timestamps * returned by this method are comparable to the timestamps returned by {@link * System#currentTimeMillis}, {@link Date#getTime}, etc. * * @throws IllegalArgumentException if {@code uuid} is not a version 1 UUID. */ public static long unixTimestamp(UUID uuid) { if (uuid.version() != 1) { throw new IllegalArgumentException( String.format( "Can only retrieve the unix timestamp for version 1 uuid (provided version %d)", uuid.version())); } long timestamp = uuid.timestamp(); return (timestamp / 10000) + START_EPOCH; } private static long makeMsb(long timestamp) { long msb = 0L; msb |= (0x00000000ffffffffL & timestamp) << 32; msb |= (0x0000ffff00000000L & timestamp) >>> 16; msb |= (0x0fff000000000000L & timestamp) >>> 48; msb |= 0x0000000000001000L; // sets the version to 1. return msb; } // Use {@link System#currentTimeMillis} for a base time in milliseconds, and if we are in the same // millisecond as the previous generation, increment the number of nanoseconds. // However, since the precision is 100-nanosecond intervals, we can only generate 10K UUIDs within // a millisecond safely. If we detect we have already generated that much UUIDs within a // millisecond (which, while admittedly unlikely in a real application, is very achievable on even // modest machines), then we stall the generator (busy spin) until the next millisecond as // required by the RFC. private static long getCurrentTimestamp() { while (true) { long now = fromUnixTimestamp(System.currentTimeMillis()); long last = lastTimestamp.get(); if (now > last) { if (lastTimestamp.compareAndSet(last, now)) { return now; } } else { long lastMillis = millisOf(last); // If the clock went back in time, bail out if (millisOf(now) < millisOf(last)) { return lastTimestamp.incrementAndGet(); } long candidate = last + 1; // If we've generated more than 10k uuid in that millisecond, restart the whole process // until we get to the next millis. Otherwise, we try use our candidate ... unless we've // been beaten by another thread in which case we try again. if (millisOf(candidate) == lastMillis && lastTimestamp.compareAndSet(last, candidate)) { return candidate; } } } } private static long fromUnixTimestamp(long tstamp) { return (tstamp - START_EPOCH) * 10000; } private static long millisOf(long timestamp) { return timestamp / 10000; } private static long makeEpoch() { // UUID v1 timestamps must be in 100-nanoseconds interval since 00:00:00.000 15 Oct 1582. 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); return c.getTimeInMillis(); } private static long makeClockSeqAndNode() { long clock = new Random(System.currentTimeMillis()).nextLong(); long node = makeNode(); long lsb = 0; lsb |= (clock & 0x0000000000003FFFL) << 48; lsb |= 0x8000000000000000L; lsb |= node; return lsb; } private static long makeNode() { // We don't have access to the MAC address (in pure JAVA at least) but need to generate a node // part that identifies this host as uniquely as possible. // The spec says that one option is to take as many sources that identify this node as possible // and hash them together. That's what we do here by gathering all the IPs of this host as well // as a few other sources. try { MessageDigest digest = MessageDigest.getInstance("MD5"); for (String address : getAllLocalAddresses()) update(digest, address); Properties props = System.getProperties(); update(digest, props.getProperty("java.vendor")); update(digest, props.getProperty("java.vendor.url")); update(digest, props.getProperty("java.version")); update(digest, props.getProperty("os.arch")); update(digest, props.getProperty("os.name")); update(digest, props.getProperty("os.version")); update(digest, getProcessPiece()); byte[] hash = digest.digest(); long node = 0; for (int i = 0; i < 6; i++) node |= (0x00000000000000ffL & (long) hash[i]) << (i * 8); // Since we don't use the MAC address, the spec says that the multicast bit (least significant // bit of the first byte of the node ID) must be 1. return node | 0x0000010000000000L; } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } } private static Set getAllLocalAddresses() { Set allIps = new HashSet<>(); try { InetAddress localhost = InetAddress.getLocalHost(); allIps.add(localhost.toString()); // Also return the hostname if available, it won't hurt (this does a dns lookup, it's only // done once at startup) allIps.add(localhost.getCanonicalHostName()); InetAddress[] allMyIps = InetAddress.getAllByName(localhost.getCanonicalHostName()); if (allMyIps != null) { for (InetAddress allMyIp : allMyIps) { allIps.add(allMyIp.toString()); } } } catch (UnknownHostException e) { // Ignore, we'll try the network interfaces anyway } try { Enumeration en = NetworkInterface.getNetworkInterfaces(); if (en != null) { while (en.hasMoreElements()) { Enumeration enumIpAddr = en.nextElement().getInetAddresses(); while (enumIpAddr.hasMoreElements()) { allIps.add(enumIpAddr.nextElement().toString()); } } } } catch (SocketException e) { // Ignore, if we've really got nothing so far, we'll throw an exception } return allIps; } private static void update(MessageDigest digest, String value) { if (value != null) { digest.update(value.getBytes(StandardCharsets.UTF_8)); } } private static String getProcessPiece() { int pid; try { @SuppressWarnings("StringSplitter") String pidJmx = ManagementFactory.getRuntimeMXBean().getName().split("@")[0]; pid = Integer.parseInt(pidJmx); } catch (Exception e) { pid = new Random().nextInt(); } ClassLoader loader = Uuids.class.getClassLoader(); int loaderId = loader != null ? System.identityHashCode(loader) : 0; return Integer.toHexString(pid) + Integer.toHexString(loaderId); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy