001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.commons.utils; 018 019import java.security.*; 020import java.time.*; 021import java.util.*; 022 023/** 024 * Generator for 025 * <a class="doclink" href="https://www.rfc-editor.org/rfc/rfc9562.html#section-5.7">RFC 9562 §5.7</a> version-7 UUIDs. 026 * 027 * <p> 028 * A v7 UUID is time-ordered: a 48-bit big-endian Unix-milliseconds timestamp in the most-significant bits, followed by 029 * the RFC 9562 version (7) and variant nibbles and {@link SecureRandom}-filled random bits. No JDK API produces v7 030 * natively as of the Java 17 baseline this repo targets. 031 * 032 * <p> 033 * The {@link SecureRandom} used to fill the random bits is cached per thread ({@link ThreadLocal}) so minting is not a 034 * fresh {@link SecureRandom} construction (and potential entropy syscall) on the request path. 035 * 036 * <h5 class='section'>See Also:</h5><ul> 037 * <li class='extlink'><a class="doclink" href="https://www.rfc-editor.org/rfc/rfc9562.html">RFC 9562 — Universally Unique IDentifiers (UUIDs)</a> 038 * </ul> 039 */ 040public final class Uuid7 { 041 042 @SuppressWarnings({ 043 "java:S5164" // Process-lifetime cached SecureRandom per thread is intentional; there is no scope boundary at which remove() would apply. 044 }) 045 private static final ThreadLocal<SecureRandom> RANDOM = ThreadLocal.withInitial(SecureRandom::new); 046 047 private static final Clock SYSTEM_CLOCK = Clock.systemUTC(); 048 049 private static final long VERSION_7 = 0x7000L; // version nibble in the low 16 bits of the MSB long 050 private static final long RAND_A_MASK = 0x0FFFL; // 12 random bits alongside the version nibble 051 private static final long VARIANT_IETF = 0x2L << 62;// binary 10 in the top 2 bits of the LSB long 052 private static final long RAND_B_MASK = 0x3FFFFFFFFFFFFFFFL; // 62 random bits 053 054 private Uuid7() {} 055 056 /** 057 * Generates a new version-7 UUID using the current wall-clock time. 058 * 059 * @return A new v7 UUID. Never <jk>null</jk>. 060 */ 061 public static UUID create() { 062 return create(SYSTEM_CLOCK); 063 } 064 065 /** 066 * Generates a new version-7 UUID whose timestamp is read from the specified clock. 067 * 068 * <p> 069 * Lets tests pin the encoded timestamp with {@link Clock#fixed(Instant, ZoneId)}. The random bits are still 070 * {@link SecureRandom}-filled, so two UUIDs minted against the same fixed clock differ. 071 * 072 * @param clock The clock supplying the Unix-milliseconds timestamp. Must not be <jk>null</jk>. 073 * @return A new v7 UUID. Never <jk>null</jk>. 074 */ 075 public static UUID create(Clock clock) { 076 var random = RANDOM.get(); 077 var ts = clock.millis() & 0xFFFFFFFFFFFFL; 078 var randA = random.nextInt() & RAND_A_MASK; 079 var msb = (ts << 16) | VERSION_7 | randA; 080 var randB = random.nextLong() & RAND_B_MASK; 081 var lsb = VARIANT_IETF | randB; 082 return new UUID(msb, lsb); 083 } 084 085 /** 086 * Generates a new version-7 UUID and returns its canonical string form. 087 * 088 * @return The string form of a new v7 UUID. Never <jk>null</jk>. 089 */ 090 public static String createString() { 091 return create().toString(); 092 } 093 094 /** 095 * Extracts the 48-bit Unix-milliseconds timestamp encoded in a v7 UUID's most-significant bits. 096 * 097 * @param uuid The UUID. Must not be <jk>null</jk>. 098 * @return The encoded Unix-milliseconds timestamp. 099 */ 100 public static long timestamp(UUID uuid) { 101 return uuid.getMostSignificantBits() >>> 16; 102 } 103 104 /** 105 * White-box accessor: the {@link SecureRandom} instance cached for the current thread. For tests only. 106 * 107 * @return The current thread's cached {@link SecureRandom}. 108 */ 109 static SecureRandom currentRandom() { 110 return RANDOM.get(); 111 } 112}