mirror of
https://github.com/apache/nifi.git
synced 2025-02-06 01:58:32 +00:00
NIFI-12379 Refactored nifi-uuid5 using standard classes
- Replaced commons-codec Hex with Java HexFormat - Replaced commons-codec DigestUtils with Java MessageDigest Signed-off-by: Pierre Villard <pierre.villard.fr@gmail.com> This closes #8035.
This commit is contained in:
parent
e1942a8346
commit
026222d157
@ -112,6 +112,10 @@
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
|
@ -21,16 +21,4 @@
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>nifi-uuid5</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
@ -18,48 +18,73 @@
|
||||
package org.apache.nifi.uuid5;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HexFormat;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
|
||||
// This is based on an unreleased implementation in Apache Commons Id.
|
||||
// See http://svn.apache.org/repos/asf/commons/sandbox/id/trunk/src/java/org/apache/commons/id/uuid/UUID.java
|
||||
/**
|
||||
* UUID Version 5 generator based on RFC 4122 Section 4.3 with SHA-1 hash algorithm for names
|
||||
*/
|
||||
public class Uuid5Util {
|
||||
public static String fromString(String value, String namespace) {
|
||||
final UUID nsUUID = namespace == null ? new UUID(0, 0) : UUID.fromString(namespace);
|
||||
final byte[] nsBytes =
|
||||
ByteBuffer.wrap(new byte[16])
|
||||
.putLong(nsUUID.getMostSignificantBits())
|
||||
.putLong(nsUUID.getLeastSignificantBits())
|
||||
.array();
|
||||
private static final String NAME_DIGEST_ALGORITHM = "SHA-1";
|
||||
|
||||
final byte[] subjectBytes = value.getBytes();
|
||||
final byte[] nameBytes = ArrayUtils.addAll(nsBytes, subjectBytes);
|
||||
final byte[] nameHash = DigestUtils.sha1(nameBytes);
|
||||
final byte[] uuidBytes = Arrays.copyOf(nameHash, 16);
|
||||
private static final int NAMESPACE_LENGTH = 16;
|
||||
|
||||
uuidBytes[6] &= 0x0F;
|
||||
uuidBytes[6] |= 0x50;
|
||||
uuidBytes[8] &= 0x3f;
|
||||
uuidBytes[8] |= 0x80;
|
||||
private static final int ENCODED_LENGTH = 32;
|
||||
|
||||
final String encoded = Hex.encodeHexString(Objects.requireNonNull(uuidBytes));
|
||||
final StringBuffer sb = new StringBuffer(encoded);
|
||||
private static final char SEPARATOR = '-';
|
||||
|
||||
while (sb.length() != 32) {
|
||||
sb.insert(0, "0");
|
||||
/**
|
||||
* Generate UUID Version 5 based on SHA-1 hash of name with optional UUID namespace
|
||||
*
|
||||
* @param name Name for deterministic UUID generation with SHA-1 hash
|
||||
* @param namespace Optional namespace defaults to 0 when not provided
|
||||
* @return UUID string consisting of 36 lowercase characters
|
||||
*/
|
||||
public static String fromString(final String name, final String namespace) {
|
||||
Objects.requireNonNull(name, "Name required");
|
||||
final UUID namespaceId = namespace == null ? new UUID(0, 0) : UUID.fromString(namespace);
|
||||
|
||||
final byte[] subject = name.getBytes();
|
||||
final int nameLength = NAMESPACE_LENGTH + subject.length;
|
||||
final ByteBuffer nameBuffer = ByteBuffer.allocate(nameLength);
|
||||
nameBuffer.putLong(namespaceId.getMostSignificantBits());
|
||||
nameBuffer.putLong(namespaceId.getLeastSignificantBits());
|
||||
nameBuffer.put(subject);
|
||||
|
||||
final byte[] nameHash = getNameHash(nameBuffer.array());
|
||||
final byte[] nameUuid = Arrays.copyOf(nameHash, NAMESPACE_LENGTH);
|
||||
|
||||
nameUuid[6] &= 0x0F;
|
||||
nameUuid[6] |= 0x50;
|
||||
nameUuid[8] &= 0x3f;
|
||||
nameUuid[8] |= (byte) 0x80;
|
||||
|
||||
final String encoded = HexFormat.of().formatHex(nameUuid);
|
||||
final StringBuilder builder = new StringBuilder(encoded);
|
||||
|
||||
while (builder.length() != ENCODED_LENGTH) {
|
||||
builder.insert(0, "0");
|
||||
}
|
||||
|
||||
sb.ensureCapacity(32);
|
||||
sb.insert(8, '-');
|
||||
sb.insert(13, '-');
|
||||
sb.insert(18, '-');
|
||||
sb.insert(23, '-');
|
||||
builder.ensureCapacity(ENCODED_LENGTH);
|
||||
builder.insert(8, SEPARATOR);
|
||||
builder.insert(13, SEPARATOR);
|
||||
builder.insert(18, SEPARATOR);
|
||||
builder.insert(23, SEPARATOR);
|
||||
|
||||
return sb.toString();
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static byte[] getNameHash(final byte[] name) {
|
||||
try {
|
||||
final MessageDigest messageDigest = MessageDigest.getInstance(NAME_DIGEST_ALGORITHM);
|
||||
return messageDigest.digest(name);
|
||||
} catch (final NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException("UUID Name Digest Algorithm not found", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.nifi.uuid5;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class Uuid5UtilTest {
|
||||
private static final String NAMESPACE = "11111111-1111-1111-1111-111111111111";
|
||||
|
||||
private static final String NAME = "identifier";
|
||||
|
||||
private static final String NAME_UUID_NULL_NAMESPACE = "485f3c34-2c6c-50a3-9904-63233467b96a";
|
||||
|
||||
private static final String NAME_UUID_NAMESPACE = "c9b54096-3890-53cc-b375-af7e7ec5e59f";
|
||||
|
||||
@Test
|
||||
void testFromStringNullNamespace() {
|
||||
final String uuid = Uuid5Util.fromString(NAME, null);
|
||||
|
||||
assertEquals(NAME_UUID_NULL_NAMESPACE, uuid);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFromStringNamespace() {
|
||||
final String uuid = Uuid5Util.fromString(NAME, NAMESPACE);
|
||||
|
||||
assertEquals(NAME_UUID_NAMESPACE, uuid);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user