refactor: change to MIT license and reformat codes

This commit is contained in:
zihluwang
2025-06-17 23:07:52 +08:00
parent 7ea6646e89
commit a878422889
73 changed files with 1521 additions and 2522 deletions
+17 -11
View File
@@ -1,19 +1,25 @@
/*
* Copyright (C) 2024-2025 OnixByte.
* Copyright (c) 2024-2025 OnixByte
*
* 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
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* http://www.apache.org/licenses/LICENSE-2.0
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* 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.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import java.net.URI
plugins {
@@ -1,25 +1,30 @@
/*
* Copyright (C) 2024-2025 OnixByte.
* Copyright (c) 2024-2025 OnixByte
*
* 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
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* http://www.apache.org/licenses/LICENSE-2.0
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* 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.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.onixbyte.identitygenerator;
/**
* The {@code IdentityGenerator} is a generic interface for generating globally unique identifiers (GUIDs)
* of a specific type.
* The {@code IdentityGenerator} is a generic interface for generating globally unique identifiers
* (GUIDs) of a specific type.
* <p>
* The type of ID is determined by the class implementing this interface.
* </p>
@@ -45,9 +50,8 @@ package com.onixbyte.identitygenerator;
* }</pre>
*
* @param <IdType> this represents the type of the Global Unique Identifier
* @author Zihlu Wang
* @version 1.1.0
* @since 1.0.0
* @author zihluwang
* @version 3.0.0
*/
public interface IdentityGenerator<IdType> {
@@ -1,18 +1,23 @@
/*
* Copyright (C) 2024-2025 OnixByte.
* Copyright (c) 2024-2025 OnixByte
*
* 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
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* http://www.apache.org/licenses/LICENSE-2.0
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* 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.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.onixbyte.identitygenerator.exceptions;
@@ -25,8 +30,8 @@ package com.onixbyte.identitygenerator.exceptions;
* provides a description of the exception, while the cause represents the underlying cause of the
* exception and provides additional information about the error.
*
* @author Zihlu Wang
* @since 1.0.0
* @author zihluwang
* @since 3.0.0
*/
public class TimingException extends RuntimeException {
@@ -1,18 +1,23 @@
/*
* Copyright (C) 2024-2025 OnixByte.
* Copyright (c) 2024-2025 OnixByte
*
* 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
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* http://www.apache.org/licenses/LICENSE-2.0
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* 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.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.onixbyte.identitygenerator.impl;
@@ -24,15 +29,20 @@ import java.security.SecureRandom;
import java.util.UUID;
/**
* A {@code SequentialUuidGenerator} is responsible for generating UUIDs following the UUID version 7 specification, which
* combines a timestamp with random bytes to create time-ordered unique identifiers.
* A {@code SequentialUuidGenerator} is responsible for generating UUIDs following the
* UUID version 7 specification, which combines a timestamp with random bytes to create time-ordered
* unique identifiers.
* <p>
* This implementation utilises a cryptographically strong {@link SecureRandom} instance to produce the random
* component of the UUID. The first 6 bytes of the UUID encode the current timestamp in milliseconds, ensuring that
* generated UUIDs are roughly ordered by creation time.
* This implementation utilises a cryptographically strong {@link SecureRandom} instance to produce
* the random component of the UUID. The first 6 bytes of the UUID encode the current timestamp in
* milliseconds, ensuring that generated UUIDs are roughly ordered by creation time.
* <p>
* The generated UUID adheres strictly to the layout and variant bits of UUID version 7 as defined in the specification.
* </p>
* The generated UUID adheres strictly to the layout and variant bits of UUID version 7 as defined
* in the specification.
*
* @author zihluwang
* @author siujamo
* @version 3.0.0
*/
public class SequentialUuidGenerator implements IdentityGenerator<UUID> {
@@ -1,18 +1,23 @@
/*
* Copyright (C) 2024-2025 OnixByte.
* Copyright (c) 2024-2025 OnixByte
*
* 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
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* http://www.apache.org/licenses/LICENSE-2.0
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* 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.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.onixbyte.identitygenerator.impl;
@@ -35,129 +40,17 @@ import java.time.ZoneId;
* <li>12 bits for sequence number (per millisecond)</li>
* </ul>
* <p>
* When initializing a {@link SnowflakeIdentityGenerator}, you must provide the worker ID and data centre
* ID, ensuring they are within the valid range defined by the bit size. The generator maintains an
* internal sequence number that increments for IDs generated within the same millisecond. If the
* system clock moves backward, an exception is thrown to prevent generating IDs with
* repeated timestamps.
* When initializing a {@link SnowflakeIdentityGenerator}, you must provide the worker ID and data
* centre ID, ensuring they are within the valid range defined by the bit size. The generator
* maintains an internal sequence number that increments for IDs generated within the
* same millisecond. If the system clock moves backward, an exception is thrown to prevent
* generating IDs with repeated timestamps.
*
* @author Zihlu Wang
* @version 1.1.0
* @since 1.0.0
* @author zihluwang
* @version 3.0.0
*/
public final class SnowflakeIdentityGenerator implements IdentityGenerator<Long> {
/**
* Constructs a SnowflakeGuidGenerator with the default start epoch and custom worker ID, data
* centre ID.
*
* @param dataCentreId the data centre ID (between 0 and 31)
* @param workerId the worker ID (between 0 and 31)
*/
public SnowflakeIdentityGenerator(long dataCentreId, long workerId) {
this(dataCentreId, workerId, DEFAULT_CUSTOM_EPOCH);
}
/**
* Constructs a SnowflakeGuidGenerator with a custom epoch, worker ID, and data centre ID.
*
* @param dataCentreId the data centre ID (between 0 and 31)
* @param workerId the worker ID (between 0 and 31)
* @param startEpoch the custom epoch timestamp (in milliseconds) to start generating IDs from
* @throws IllegalArgumentException if the start epoch is greater than the current timestamp,
* or if the worker ID or data centre ID is out of range
*/
public SnowflakeIdentityGenerator(long dataCentreId, long workerId, long startEpoch) {
if (startEpoch > currentTimestamp()) {
throw new IllegalArgumentException("Start Epoch can not be greater than current timestamp!");
}
var maxWorkerId = ~(-1L << workerIdBits);
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("Worker Id can't be greater than %d or less than 0",
maxWorkerId));
}
var maxDataCentreId = ~(-1L << dataCentreIdBits);
if (dataCentreId > maxDataCentreId || dataCentreId < 0) {
throw new IllegalArgumentException(String.format("Data Centre Id can't be greater than %d or less than 0",
maxDataCentreId));
}
this.startEpoch = startEpoch;
this.workerId = workerId;
this.dataCentreId = dataCentreId;
}
/**
* Generates the next unique ID.
*
* @return the generated unique ID
* @throws TimingException if the system clock moves backwards, indicating an invalid sequence
* of timestamps.
*/
@Override
public synchronized Long nextId() {
var timestamp = currentTimestamp();
// if the current time is less than the timestamp of the last ID generation, it means that
// the system clock has been set back and an exception should be thrown
if (timestamp < lastTimestamp) {
throw new TimingException("Clock moved backwards. Refusing to generate id for %d milliseconds"
.formatted(lastTimestamp - timestamp));
}
// if generated at the same time, perform intra-millisecond sequences
long sequenceBits = 12L;
if (lastTimestamp == timestamp) {
long sequenceMask = ~(-1L << sequenceBits);
sequence = (sequence + 1) & sequenceMask;
// sequence overflow in milliseconds
if (sequence == 0) {
// block to the next millisecond, get a new timestamp
timestamp = awaitToNextMillis(lastTimestamp);
}
}
// timestamp change, sequence reset in milliseconds
else {
sequence = 0L;
}
// timestamp of last ID generation
lastTimestamp = timestamp;
// shifted and put together by or operations to form a 64-bit ID
var timestampLeftShift = sequenceBits + workerIdBits + dataCentreIdBits;
var dataCentreIdShift = sequenceBits + workerIdBits;
return ((timestamp - startEpoch) << timestampLeftShift)
| (dataCentreId << dataCentreIdShift)
| (workerId << sequenceBits)
| sequence;
}
/**
* Blocks until the next millisecond to obtain a new timestamp.
*
* @param lastTimestamp the timestamp when the last ID was generated
* @return the current timestamp
*/
private long awaitToNextMillis(long lastTimestamp) {
var timestamp = currentTimestamp();
while (timestamp <= lastTimestamp) {
timestamp = currentTimestamp();
}
return timestamp;
}
/**
* Returns the current timestamp in milliseconds.
*
* @return the current timestamp
*/
private long currentTimestamp() {
return LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
}
/**
* Default custom epoch.
*
@@ -200,5 +93,116 @@ public final class SnowflakeIdentityGenerator implements IdentityGenerator<Long>
*/
private long lastTimestamp = -1L;
/**
* Constructs a SnowflakeGuidGenerator with the default start epoch and custom worker ID, data
* centre ID.
*
* @param dataCentreId the data centre ID (between 0 and 31)
* @param workerId the worker ID (between 0 and 31)
*/
public SnowflakeIdentityGenerator(long dataCentreId, long workerId) {
this(dataCentreId, workerId, DEFAULT_CUSTOM_EPOCH);
}
/**
* Constructs a SnowflakeGuidGenerator with a custom epoch, worker ID, and data centre ID.
*
* @param dataCentreId the data centre ID (between 0 and 31)
* @param workerId the worker ID (between 0 and 31)
* @param startEpoch the custom epoch timestamp (in milliseconds) to start generating IDs from
* @throws IllegalArgumentException if the start epoch is greater than the current timestamp,
* or if the worker ID or data centre ID is out of range
*/
public SnowflakeIdentityGenerator(long dataCentreId, long workerId, long startEpoch) {
if (startEpoch > currentTimestamp()) {
throw new IllegalArgumentException("Start Epoch can not be greater than current timestamp!");
}
var maxWorkerId = ~(-1L << workerIdBits);
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("Worker Id can't be greater than %d or less than 0",
maxWorkerId));
}
var maxDataCentreId = ~(-1L << dataCentreIdBits);
if (dataCentreId > maxDataCentreId || dataCentreId < 0) {
throw new IllegalArgumentException(String.format("Data Centre Id can't be greater than %d or less than 0",
maxDataCentreId));
}
this.startEpoch = startEpoch;
this.workerId = workerId;
this.dataCentreId = dataCentreId;
}
/**
* Generates the next unique ID.
*
* @return the generated unique ID
* @throws TimingException if the system clock moves backwards, indicating an invalid sequence
* of timestamps.
*/
@Override
public synchronized Long nextId() {
var timestamp = currentTimestamp();
// if the current time is less than the timestamp of the last ID generation, it means that
// the system clock has been set back and an exception should be thrown
if (timestamp < lastTimestamp) {
throw new TimingException("Clock moved backwards. Refusing to generate id for %d milliseconds"
.formatted(lastTimestamp - timestamp));
}
// if generated at the same time, perform intra-millisecond sequences
long sequenceBits = 12L;
if (lastTimestamp == timestamp) {
long sequenceMask = ~(-1L << sequenceBits);
sequence = (sequence + 1) & sequenceMask;
// sequence overflow in milliseconds
if (sequence == 0) {
// block to the next millisecond, get a new timestamp
timestamp = awaitToNextMillis(lastTimestamp);
}
}
// timestamp change, sequence reset in milliseconds
else {
sequence = 0L;
}
// timestamp of last ID generation
lastTimestamp = timestamp;
// shifted and put together by or operations to form a 64-bit ID
var timestampLeftShift = sequenceBits + workerIdBits + dataCentreIdBits;
var dataCentreIdShift = sequenceBits + workerIdBits;
return ((timestamp - startEpoch) << timestampLeftShift)
| (dataCentreId << dataCentreIdShift)
| (workerId << sequenceBits)
| sequence;
}
/**
* Blocks until the next millisecond to obtain a new timestamp.
*
* @param lastTimestamp the timestamp when the last ID was generated
* @return the current timestamp
*/
private long awaitToNextMillis(long lastTimestamp) {
var timestamp = currentTimestamp();
while (timestamp <= lastTimestamp) {
timestamp = currentTimestamp();
}
return timestamp;
}
/**
* Returns the current timestamp in milliseconds.
*
* @return the current timestamp
*/
private long currentTimestamp() {
return LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
}
}
@@ -1,25 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (C) 2024-2025 OnixByte.
~ Copyright (c) 2024-2025 OnixByte
~
~ 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
~ Permission is hereby granted, free of charge, to any person obtaining a copy
~ of this software and associated documentation files (the "Software"), to deal
~ in the Software without restriction, including without limitation the rights
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
~ copies of the Software, and to permit persons to whom the Software is
~ furnished to do so, subject to the following conditions:
~
~ http://www.apache.org/licenses/LICENSE-2.0
~ The above copyright notice and this permission notice shall be included in all
~ copies or substantial portions of the Software.
~
~ 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.
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
~ SOFTWARE.
-->
<configuration>
<property name="COLOURFUL_OUTPUT" value="%black(%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK}) %highlight(%-5level) %black(---) %black([%10.10t]) %cyan(%-20.20logger{20}) %black(:) %msg%n"/>
<property name="STANDARD_OUTPUT" value="%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK} %-5level %black(---) [%10.10t] %-20.20logger{20} : %msg%n"/>
<statusListener class="ch.qos.logback.core.status.NopStatusListener" />
<configuration>
<property name="COLOURFUL_OUTPUT"
value="%black(%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK}) %highlight(%-5level) %black(---) %black([%10.10t]) %cyan(%-20.20logger{20}) %black(:) %msg%n"/>
<property name="STANDARD_OUTPUT"
value="%date{'dd MMM, yyyy HH:mm:ss', Asia/Hong_Kong, en-UK} %-5level %black(---) [%10.10t] %-20.20logger{20} : %msg%n"/>
<statusListener class="ch.qos.logback.core.status.NopStatusListener"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${COLOURFUL_OUTPUT}</pattern>
@@ -28,4 +36,4 @@
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
</configuration>