private NodeIdSupplier nodeIdSupplier;
public AbstractAsyncUuidFactory() {
- secureRandom = newSecureRandom();
+ secureRandom = createSecureRandom();
timeUuidState = new ConcurrentTimeUuidState(secureRandom, null);
}
/*
* ABSTRACT METHODS
*/
- protected abstract SecureRandom newSecureRandom();
+ protected abstract SecureRandom createSecureRandom();
/*
* STATE
/*
* SYNC OPERATIONS
*/
- protected UUID newRandomUUIDStrong() {
+ protected UUID createRandomUUIDStrong() {
SecureRandomParameters parameters = secureRandom.getParameters();
if (parameters != null) {
if (parameters instanceof DrbgParameters.Instantiation) {
}
}
}
- return newRandomUUID(secureRandom);
+ return createRandomUUID(secureRandom);
}
public UUID randomUUIDWeak() {
- return newRandomUUID(ThreadLocalRandom.current());
+ return createRandomUUID(ThreadLocalRandom.current());
}
- protected UUID newTimeUUID() {
+ protected UUID createTimeUUID() {
if (nodeIdSupplier == null)
throw new IllegalStateException("No node id supplier available");
UUID uuid = new UUID(timeUuidState.getMostSignificantBits(), timeUuidState.getLeastSignificantBits());
@Override
public ForkJoinTask<UUID> futureNameUUIDv5(UUID namespace, byte[] data) {
- return submit(() -> newNameUUIDv5(namespace, data));
+ return submit(() -> createNameUUIDv5(namespace, data));
}
@Override
public ForkJoinTask<UUID> futureNameUUIDv3(UUID namespace, byte[] data) {
- return submit(() -> newNameUUIDv3(namespace, data));
+ return submit(() -> createNameUUIDv3(namespace, data));
}
@Override
public ForkJoinTask<UUID> futureRandomUUIDStrong() {
- return submit(this::newRandomUUIDStrong);
+ return submit(this::createRandomUUIDStrong);
}
@Override
public ForkJoinTask<UUID> futureTimeUUID() {
- return submit(this::newTimeUUID);
+ return submit(this::createTimeUUID);
}
}
private final static long MOST_SIG_VERSION1 = (1l << 12);
private final static long LEAST_SIG_RFC4122_VARIANT = (1l << 63);
- protected UUID newTimeUUID(long timestamp, long clockSequence, byte[] node, int offset) {
+ @Deprecated
+ protected UUID createTimeUUID(long timestamp, long clockSequence, byte[] node, int offset) {
Objects.requireNonNull(node, "Node array cannot be null");
if (node.length < offset + 6)
throw new IllegalArgumentException("Node array must be at least 6 bytes long");
return uuid;
}
+ @Deprecated
protected UUID timeUUID(Temporal time, long clockSequence, byte[] node, int offset) {
// TODO add checks
Duration duration = Duration.between(TimeUuid.TIMESTAMP_ZERO, time);
// Number of 100 ns intervals in one second: 1000000000 / 100 = 10000000
long timestamp = duration.getSeconds() * 10000000 + duration.getNano() / 100;
- return newTimeUUID(timestamp, clockSequence, node, offset);
+ return createTimeUUID(timestamp, clockSequence, node, offset);
}
/*
* NAME BASED (version 3 and 5)
*/
+ protected UUID createNameUUIDv5(UUID namespace, byte[] name) {
+ return createNameUUIDv5Static(namespace, name);
+ }
- protected UUID newNameUUIDv5(UUID namespace, byte[] name) {
+ static UUID createNameUUIDv5Static(UUID namespace, byte[] name) {
Objects.requireNonNull(namespace, "Namespace cannot be null");
Objects.requireNonNull(name, "Name cannot be null");
return result;
}
- protected UUID newNameUUIDv3(UUID namespace, byte[] name) {
+ protected UUID createNameUUIDv3(UUID namespace, byte[] name) {
+ return createNameUUIDv3Static(namespace, name);
+ }
+
+ static UUID createNameUUIDv3Static(UUID namespace, byte[] name) {
Objects.requireNonNull(namespace, "Namespace cannot be null");
Objects.requireNonNull(name, "Name cannot be null");
/*
* RANDOM v4
*/
- protected UUID newRandomUUID(Random random) {
+ protected UUID createRandomUUID(Random random) {
byte[] arr = new byte[16];
random.nextBytes(arr);
arr[6] &= 0x0f;
--- /dev/null
+package org.argeo.api.uuid;
+
+import java.util.UUID;
+
+/** A name based UUID (v3 or v5) whose construction values are not known. */
+public class BasicNameUuid extends TypedUuid {
+ private static final long serialVersionUID = APM.SERIAL;
+
+ public BasicNameUuid(UUID uuid) {
+ super(uuid);
+ if ((uuid.version() != 5 && uuid.version() != 3) || uuid.variant() != 2)
+ throw new IllegalArgumentException("The provided UUID is not a name-based UUID.");
+ }
+
+ /**
+ * Always returns <code>true</true> since it is unknown from which values it was
+ * constructed..
+ */
+ @Override
+ public boolean isOpaque() {
+ return true;
+ }
+
+ /**
+ * Whether the hash of this name UUID was generated with SHA-1 (v5) or with MD5
+ * (v3).
+ */
+ public boolean isSha1() {
+ return uuid.version() == 5;
+ }
+}
--- /dev/null
+package org.argeo.api.uuid;
+
+import java.util.UUID;
+
+/**
+ * A name UUID whose binary data used for its construction is known. A new byte
+ * array is created and it is copied when retrieved, so this would be
+ * inefficient and memory consuming for big data amounts. This rather meant to
+ * be used for small binary data, such as certificates, etc.
+ */
+public class BinaryNameUuid extends BasicNameUuid {
+ private static final long serialVersionUID = APM.SERIAL;
+
+ protected final TypedUuid namespace;
+ protected final byte[] bytes;
+
+ /** Static builder (a {@link TypedUuidFactory} may be more efficient). */
+ public BinaryNameUuid(TypedUuid namespace, byte[] bytes, boolean sha1) {
+ this(sha1 ? AbstractUuidFactory.createNameUUIDv5Static(namespace.uuid, bytes)
+ : AbstractUuidFactory.createNameUUIDv5Static(namespace.uuid, bytes), namespace, bytes);
+ }
+
+ /**
+ * Since no check is performed, the constructor is protected so that the object
+ * can only be built by the default methods of {@link TypedUuidFactory} (in this
+ * package) or by extending the class.
+ */
+ protected BinaryNameUuid(UUID uuid, TypedUuid namespace, byte[] bytes) {
+ super(uuid);
+ this.namespace = namespace;
+ this.bytes = new byte[bytes.length];
+ System.arraycopy(bytes, 0, this.bytes, 0, bytes.length);
+ }
+
+ /** The namespace used to build this UUID. */
+ public final TypedUuid getNamespace() {
+ return namespace;
+ }
+
+ /**
+ * A <strong>copy</strong> of the bytes which have been hashed. In order to
+ * access the byte array directly, the class must be extended.
+ */
+ public final byte[] getBytes() {
+ byte[] bytes = new byte[this.bytes.length];
+ System.arraycopy(this.bytes, 0, bytes, 0, this.bytes.length);
+ return bytes;
+ }
+
+ /** Always returns <code>false</code> since the construction value is known. */
+ @Override
+ public final boolean isOpaque() {
+ return false;
+ }
+
+}
*
* @see https://datatracker.ietf.org/doc/html/rfc4122
*/
-public class ConcurrentUuidFactory extends AbstractAsyncUuidFactory {
+public class ConcurrentUuidFactory extends AbstractAsyncUuidFactory implements TypedUuidFactory {
private final static Logger logger = System.getLogger(ConcurrentUuidFactory.class.getName());
public ConcurrentUuidFactory(byte[] nodeId) {
}
@Override
- protected SecureRandom newSecureRandom() {
+ protected SecureRandom createSecureRandom() {
SecureRandom secureRandom;
try {
secureRandom = SecureRandom.getInstance("DRBG",
--- /dev/null
+package org.argeo.api.uuid;
+
+import java.util.UUID;
+
+/**
+ * A variant 6 {@link UUID}.
+ *
+ * @see https://datatracker.ietf.org/doc/html/rfc4122#section-4.1.1
+ */
+public class GUID extends TypedUuid {
+ private static final long serialVersionUID = APM.SERIAL;
+
+ /** Constructor based on a {@link UUID}. */
+ public GUID(UUID uuid) {
+ super(uuid);
+ if (uuid.variant() != 6)
+ throw new IllegalArgumentException("The provided UUID is not a GUID.");
+ }
+
+ /**
+ * Formats N, D, B, P are supported:
+ * <ul>
+ * <li>D: 1db31359-bdd8-5a0f-b672-30c247d582c5</li>
+ * <li>N: 1db31359bdd85a0fb67230c247d582c5</li>
+ * <li>B: {1db31359-bdd8-5a0f-b672-30c247d582c5}</li>
+ * <li>P: (1db31359-bdd8-5a0f-b672-30c247d582c5)</li>
+ * </ul>
+ *
+ * @see https://docs.microsoft.com/en-us/dotnet/api/system.guid.tostring
+ */
+ public static String toString(UUID uuid, char format, boolean upperCase) {
+ String str;
+ switch (format) {
+ case 'D':
+ str = uuid.toString();
+ break;
+ case 'N':
+ str = UuidBinaryUtils.toCompact(uuid);
+ break;
+ case 'B':
+ str = "{" + uuid.toString() + "}";
+ break;
+ case 'P':
+ str = "(" + uuid.toString() + ")";
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported format : " + format);
+ }
+ return upperCase ? str.toUpperCase() : str;
+ }
+
+}
package org.argeo.api.uuid;
import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
import java.util.UUID;
-/** A name {@link UUID} whose values used for its construction are known. */
-public class NameUuid extends UnkownNameUuid {
+/** A name UUID whose values used for its construction are known. */
+public class NameUuid extends BasicNameUuid {
private static final long serialVersionUID = APM.SERIAL;
protected final TypedUuid namespace;
protected final String name;
protected final Charset encoding;
+ /**
+ * Default static builder which creates a v5 (SHA1) name based {@link UUID},
+ * using UTF-8 encoding. Use
+ * {@link #NameUuid(TypedUuid, String, Charset, boolean)} for more options.
+ */
+ public NameUuid(TypedUuid namespace, String name) {
+ this(namespace, name, StandardCharsets.UTF_8, true);
+ }
+
+ /** Static builder (an {@link TypedUuidFactory} may be more efficient). */
+ public NameUuid(TypedUuid namespace, String name, Charset encoding, boolean sha1) {
+ this(sha1 ? AbstractUuidFactory.createNameUUIDv5Static(namespace.uuid, name.getBytes(encoding))
+ : AbstractUuidFactory.createNameUUIDv5Static(namespace.uuid, name.getBytes(encoding)), namespace, name,
+ encoding);
+ }
+
+ /**
+ * Since no check is performed, the constructor is protected so that the object
+ * can only be built by the default methods of {@link TypedUuidFactory} (in this
+ * package) or by extending the class.
+ */
protected NameUuid(UUID uuid, TypedUuid namespace, String name, Charset encoding) {
super(uuid);
assert namespace != null;
this.encoding = encoding;
}
- public static long getSerialversionuid() {
- return serialVersionUID;
- }
-
/** The namespace used to build this UUID. */
public final TypedUuid getNamespace() {
return namespace;
public final class RandomUuid extends TypedUuid {
private static final long serialVersionUID = APM.SERIAL;
+ /** Constructor based on a {@link UUID}. */
public RandomUuid(UUID uuid) {
super(uuid);
if (uuid.version() != 4 && uuid.variant() != 2)
*/
public final static Instant TIMESTAMP_ZERO = ZonedDateTime.of(1582, 10, 15, 0, 0, 0, 0, ZoneOffset.UTC).toInstant();
+ /** Constructor based on a {@link UUID}. */
public TimeUuid(UUID uuid) {
super(uuid);
if (uuid.version() != 1 && uuid.variant() != 2)
import java.util.UUID;
/**
- * Base class for objects which are explicitly typed, based on the various UUID
- * versions. Such a derivation hierarchy still represents the {@link UUID}
+ * Base class for objects which are explicitly typed, based on the various
+ * variant 2 (RFC 4122) UUID versions (and variant 6 with {@link GUID}, for
+ * completion). Such a derivation hierarchy still represents the {@link UUID}
* itself, not the objects, data or concept that it identifies. Just like
* {@link UUID}s, {@link TypedUuid} should be used as identifier, not as base
* class for complex objects. It should rather be seen as a framework to build
/**
* Constructs a {@link TypedUuid} of the most appropriate subtype, based on this
- * {@link UUID}.
+ * {@link UUID}. For name based UUIDs, it will return an opaque
+ * {@link BasicNameUuid}; {@link NameUuid} and {@link BinaryNameUuid} may be
+ * more useful.
*/
public static TypedUuid of(UUID uuid) {
Objects.requireNonNull(uuid, "UUID cannot be null");
return new RandomUuid(uuid);
case 3:
case 5:
- return new UnkownNameUuid(uuid);
+ return new BasicNameUuid(uuid);
default:
throw new IllegalArgumentException("UUIDs with version " + uuid.version() + " are not supported.");
}
- } else if (uuid.variant() == 6) {// Microsoft
- throw new IllegalArgumentException("Microsoft UUIDs (aka. GUIDs) are not supported.");
+ } else if (uuid.variant() == 6) {// GUID
+ return new GUID(uuid);
} else {
throw new IllegalArgumentException("UUIDs with variant " + uuid.variant() + " are not supported.");
}
}
+
}
--- /dev/null
+package org.argeo.api.uuid;
+
+import java.nio.charset.Charset;
+
+/** An {@link UuidFactory} which also (trivially) produces {@link TypedUuid}. */
+public interface TypedUuidFactory extends UuidFactory {
+ /** Creates a {@link TimeUuid} (v1). */
+ default TimeUuid newTimeUuid() {
+ return new TimeUuid(timeUUID());
+ }
+
+ /** Creates an MD5 {@link NameUuid} (v3). */
+ default NameUuid newNameUuidV3(TypedUuid namespace, String name, Charset charset) {
+ return new NameUuid(nameUUIDv3(namespace.get(), name, charset), namespace, name, charset);
+ }
+
+ /** Creates a {@link RandomUuid}, using {@link #randomUUID()}. */
+ default RandomUuid newRandomUuid() {
+ return new RandomUuid(randomUUID());
+ }
+
+ /** Creates an SHA1 {@link NameUuid} (v5). */
+ default NameUuid newNameUuidV5(TypedUuid namespace, String name, Charset charset) {
+ return new NameUuid(nameUUIDv5(namespace.get(), name, charset), namespace, name, charset);
+ }
+}
+++ /dev/null
-package org.argeo.api.uuid;
-
-import java.util.UUID;
-
-/** A name-based UUID (v3 or v5) whose construction values are not known. */
-public class UnkownNameUuid extends TypedUuid {
- private static final long serialVersionUID = APM.SERIAL;
-
- public UnkownNameUuid(UUID uuid) {
- super(uuid);
- if ((uuid.version() != 5 && uuid.version() != 3) || uuid.variant() != 2)
- throw new IllegalArgumentException("The provided UUID is not a name-based UUID.");
- }
-
- /**
- * Always returns <code>true</true> since it is unknown from which values it was
- * constructed..
- */
- @Override
- public boolean isOpaque() {
- return true;
- }
-
- /**
- * Whether the hash of this name UUID was generated with SHA-1 (v5) or with MD5
- * (v3).
- */
- public boolean isSha1() {
- return uuid.version() == 5;
- }
-}
public CmsUuidFactory(byte[] nodeId) {
super(nodeId);
- assert newTimeUUID().node() == BitSet.valueOf(toNodeIdBytes(nodeId, 0)).toLongArray()[0];
+ assert createTimeUUID().node() == BitSet.valueOf(toNodeIdBytes(nodeId, 0)).toLongArray()[0];
}
public CmsUuidFactory() {