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

org.apache.axiom.util.UIDGenerator Maven / Gradle / Ivy

There is a newer version: 5.0.22
Show 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.axiom.util;

import java.net.URI;
import java.net.URISyntaxException;
import java.security.SecureRandom;
import java.util.Random;
import java.util.UUID;

/**
 * Contains utility methods to generate unique IDs of various kinds.
 * 

* Depending on the requested type of ID, this class will either use * {@link UUID#randomUUID()} (or an equivalent algorithm) * or its own unique ID generator. This implementation * generates unique IDs based on the assumption that the following triplet is * unique: *

    *
  1. The thread ID. *
  2. The timestamp in milliseconds when the first UID is requested by the * thread. *
  3. A per thread sequence number that is incremented each time a UID is * requested by the thread. *
*

* Considering that these three numbers are represented as long * values, these assumptions are correct because: *

    *
  • The probability that two different threads with the same ID exist in the * same millisecond interval is negligibly small. *
  • One can expect that no thread will ever request more than 2^64 UIDs * during its lifetime. *
*

* Before building an ID from this triplet, the implementation will XOR the * three values with random values calculated once when the class is loaded. * This transformation preserves the uniqueness of the calculated triplet and * serves several purposes: *

    *
  • It reduces the probability that the same ID is produces by two different * systems, i.e. it increases global uniqueness. *
  • It adds entropy, i.e. it makes an individual ID appear as random. Indeed, * without the XOR transformation, a hexadecimal representation of the triplet * would in general contain several sequences of '0'. *
  • It prevents the implementation from leaking information about the system * state. *
*/ public final class UIDGenerator { private static final long startTimeXorOperand; private static final long threadIdXorOperand; private static final long seqXorOperand; private static final SecureRandom secureRandom = new SecureRandom(); /** * Array of 16 caches that contain random bytes fetched from {@link #secureRandom} and that are * used to compute UUIDs. These caches are used to reduce the number of calls to * {@link SecureRandom#nextBytes(byte[])}. The cache used by a given thread is determined by the * thread ID. Multiple caches are used to reduce contention between threads. */ private static final UUIDCache[] uuidCaches; static { Random rand = new Random(); threadIdXorOperand = rand.nextLong(); startTimeXorOperand = rand.nextLong(); seqXorOperand = rand.nextLong(); uuidCaches = new UUIDCache[16]; for (int i=0; i<16; i++) { uuidCaches[i] = new UUIDCache(); } } /** * Thread local that holds the triplet described in the Javadoc of this * class. Note that we use a simple array here (instead of our own class) * to avoid class loader leaks (see AXIOM-354). */ private static final ThreadLocal triplet = new ThreadLocal() { @Override protected long[] initialValue() { long[] values = new long[3]; values[0] = Thread.currentThread().getId() ^ threadIdXorOperand; values[1] = System.currentTimeMillis() ^ startTimeXorOperand; return values; } }; private UIDGenerator() {} private static void writeReverseLongHex(long value, StringBuilder buffer) { for (int i=0; i<16; i++) { int n = (int)(value >> (4*i)) & 0xF; writeNibble(n, buffer); } } private static void writeNibble(int n, StringBuilder buffer) { buffer.append((char)(n < 10 ? '0' + n : 'a' + n - 10)); } /** * Generate a unique ID as hex value and add it to the given buffer. Note * that with respect to the triplet, the order of nibbles is reversed, i.e. * the least significant nibble of the sequence is written first. This makes * comparing two IDs for equality more efficient. * * @param buffer */ private static void generateHex(StringBuilder buffer) { long[] values = triplet.get(); writeReverseLongHex(values[2]++ ^ seqXorOperand, buffer); writeReverseLongHex(values[1], buffer); writeReverseLongHex(values[0], buffer); } /** * Generates a unique ID suitable for usage as a MIME content ID. *

* RFC2045 (MIME) specifies that the value of the {@code Content-ID} header * must match the {@code msg-id} production, which is defined by RFC2822 as * follows: *

     * msg-id        = [CFWS] "<" id-left "@" id-right ">" [CFWS]
     * id-left       = dot-atom-text / no-fold-quote / obs-id-left
     * id-right      = dot-atom-text / no-fold-literal / obs-id-right
     * dot-atom-text = 1*atext *("." 1*atext)
     * atext         = ALPHA / DIGIT / "!" / "#" / "$" / "%" / "&"
     *                   / "'" / "*" / "+" / "-" / "/" / "=" / "?"
     *                   / "^" / "_" / "`" / "{" / "|" / "}" / "~"
* In addition, RFC2392 specifies that when used in an URL with scheme * "cid:", the content ID must be URL encoded. Since not all implementations * handle this correctly, any characters considered "unsafe" in an URL (and * requiring encoding) should be avoided in a content ID. *

* This method generates content IDs that satisfy these requirements. It * guarantees a high level of uniqueness, but makes no provisions to * guarantee randomness. The implementation is thread safe, but doesn't use * synchronization. * * @return The generated content ID. Note that this value does not include * the angle brackets of the {@code msg-id} production, but only * represents the bare content ID. */ public static String generateContentId() { StringBuilder buffer = new StringBuilder(); generateHex(buffer); buffer.append("@apache.org"); return buffer.toString(); } /** * Generates a MIME boundary. *

* Valid MIME boundaries are defined by the following production in RFC2046: *

     * boundary      := 0*69<bchars> bcharsnospace
     * bchars        := bcharsnospace / " "
     * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
     *                  "+" / "_" / "," / "-" / "." /
     *                  "/" / ":" / "=" / "?"
*

* It should be noted that the boundary in general will also appear as a parameter in the * content type of the MIME package. According to RFC2045 (which defines the * {@code Content-Type} header), it will require quoting if it contains characters from * the following production: *

     * tspecials := "(" / ")" / "<" / ">" / "@" /
     *              "," / ";" / ":" / "\" / <"> /
     *              "/" / "[" / "]" / "?" / "="
*

* This method produces a boundary that doesn't contain any of these characters and * therefore doesn't need to be quoted. To avoid accidental collisions, the returned value * is unique and doesn't overlap with any other type of unique ID returned by methods in * this class. The implementation is thread safe, but doesn't use synchronization. * * @return the generated MIME boundary */ public static String generateMimeBoundary() { StringBuilder buffer = new StringBuilder("MIMEBoundary_"); generateHex(buffer); return buffer.toString(); } /** * Generate a general purpose unique ID. The returned value is the hexadecimal representation of * a 192 bit value, i.e. it is 48 characters long. The implementation guarantees a high level of * uniqueness, but makes no provisions to guarantee randomness. It is thread safe, but doesn't * use synchronization. *

* The fact that this method doesn't guarantee randomness implies that the generated IDs are * predictable and must not be used in contexts where this would cause a security vulnerability. * In particular, this method should not be used to generate the following kind of IDs: *

    *
  • Session IDs. *
  • Message IDs used in WS-Addressing. *
* * @return the generated unique ID */ public static String generateUID() { StringBuilder buffer = new StringBuilder(48); generateHex(buffer); return buffer.toString(); } /** * Generate a URN with {@code uuid} NID (namespace identifier). These URNs have the following * form: {@code urn:uuid:dae6fae1-93df-4824-bc70-884c9edb5973}. The UUID is generated using * a cryptographically strong pseudo random number generator. * * @return the generated URN */ public static String generateURNString() { StringBuilder urn = new StringBuilder(45); urn.append("urn:uuid:"); UUIDCache cache = uuidCaches[(int)Thread.currentThread().getId() & 0xF]; synchronized (cache) { boolean fill; int position = cache.position; byte[] randomBytes = cache.randomBytes; if (randomBytes == null) { cache.randomBytes = randomBytes = new byte[4096]; fill = true; } else if (position == 4096) { position = 0; fill = true; } else { fill = false; } if (fill) { secureRandom.nextBytes(cache.randomBytes); } writeHex(randomBytes[position], urn); writeHex(randomBytes[position+1], urn); writeHex(randomBytes[position+2], urn); writeHex(randomBytes[position+3], urn); urn.append('-'); writeHex(randomBytes[position+4], urn); writeHex(randomBytes[position+5], urn); urn.append('-'); writeHex((byte)(randomBytes[position+6] & 0x0F | 0x40), urn); writeHex(randomBytes[position+7], urn); urn.append('-'); writeHex((byte)(randomBytes[position+8] & 0x3F | 0x80), urn); writeHex(randomBytes[position+9], urn); urn.append('-'); writeHex(randomBytes[position+10], urn); writeHex(randomBytes[position+11], urn); writeHex(randomBytes[position+12], urn); writeHex(randomBytes[position+13], urn); writeHex(randomBytes[position+14], urn); writeHex(randomBytes[position+15], urn); cache.position = position+16; } return urn.toString(); } private static void writeHex(byte b, StringBuilder buffer) { writeNibble(b >> 4 & 0xF, buffer); writeNibble(b & 0xF, buffer); } /** * Generate a URN with {@code uuid} NID (namespace identifier). This method does the * same as {@link #generateURNString()}, but returns a {@link URI} object. * * @return the generated URN */ public static URI generateURN() { try { return new URI(generateURNString()); } catch (URISyntaxException ex) { // If we ever get here, then if would mean that there is something badly broken... throw new Error(ex); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy