("devkitUtils") {
69 | groupId = group.toString()
70 | artifactId = "devkit-utils"
71 | version = artefactVersion
72 |
73 | pom {
74 | name = "DevKit - Utils"
75 | description = "The utils module of JDevKit."
76 | url = projectUrl
77 |
78 | licenses {
79 | license {
80 | name = licenseName
81 | url = licenseUrl
82 | }
83 | }
84 |
85 | scm {
86 | connection = "scm:git:git://github.com:OnixByte/JDevKit.git"
87 | developerConnection = "scm:git:git://github.com:OnixByte/JDevKit.git"
88 | url = projectGithubUrl
89 | }
90 |
91 | developers {
92 | developer {
93 | id = "zihluwang"
94 | name = "Zihlu Wang"
95 | email = "really@zihlu.wang"
96 | timezone = "Asia/Hong_Kong"
97 | }
98 |
99 | developer {
100 | id = "siujamo"
101 | name = "Siu Jam'o"
102 | email = "jamo.siu@outlook.com"
103 | timezone = "Asia/Shanghai"
104 | }
105 | }
106 | }
107 |
108 | from(components["java"])
109 |
110 | signing {
111 | sign(publishing.publications["devkitUtils"])
112 | }
113 | }
114 |
115 | repositories {
116 | maven {
117 | name = "sonatypeNexus"
118 | url = URI(providers.gradleProperty("repo.maven-central.host").get())
119 | credentials {
120 | username = providers.gradleProperty("repo.maven-central.username").get()
121 | password = providers.gradleProperty("repo.maven-central.password").get()
122 | }
123 | }
124 | }
125 | }
126 | }
--------------------------------------------------------------------------------
/devkit-utils/src/main/java/com/onixbyte/devkit/utils/BoolUtil.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.devkit.utils;
19 |
20 | import java.util.Arrays;
21 | import java.util.Objects;
22 | import java.util.function.BooleanSupplier;
23 |
24 | /**
25 | * The {@link BoolUtil} class provides utility methods for boolean calculations.
26 | * This class offers methods to perform logical operations such as AND, OR, and NOT on boolean values.
27 | *
28 | * The utility methods in this class are useful for scenarios where multiple boolean values need to be
29 | * evaluated together, and for simplifying complex boolean expressions.
30 | *
31 | *
32 | * Example usage:
33 | *
34 | * {@code
35 | * boolean result1 = BoolUtil.and(true, true, false); // false
36 | * boolean result2 = BoolUtil.or(true, false, false); // true
37 | * boolean result3 = BoolUtil.not(false); // true
38 | * }
39 | *
40 | *
41 | * @author zihluwang
42 | * @version 1.6.2
43 | * @since 1.6.2
44 | */
45 | public final class BoolUtil {
46 |
47 | /**
48 | * Logical and calculation.
49 | *
50 | * @param values the values to be calculated
51 | * @return {@code true} if all value in values is {@code true}, otherwise {@code false}
52 | */
53 | public static boolean and(Boolean... values) {
54 | return Arrays.stream(values)
55 | .filter(Objects::nonNull)
56 | .allMatch(Boolean::booleanValue);
57 | }
58 |
59 | /**
60 | * Logical and calculation.
61 | *
62 | * @param valueSuppliers the suppliers of value to be calculated
63 | * @return {@code true} if all value in values is {@code true}, otherwise {@code false}
64 | */
65 | public static boolean and(BooleanSupplier... valueSuppliers) {
66 | return Arrays.stream(valueSuppliers)
67 | .filter(Objects::nonNull)
68 | .allMatch(BooleanSupplier::getAsBoolean);
69 | }
70 |
71 | /**
72 | * Logical or calculation.
73 | *
74 | * @param values the values to be calculated
75 | * @return {@code true} if any value in values is {@code true}, otherwise {@code false}
76 | */
77 | public static boolean or(Boolean... values) {
78 | return Arrays.stream(values)
79 | .filter(Objects::nonNull)
80 | .anyMatch(Boolean::booleanValue);
81 | }
82 |
83 | /**
84 | * Logical or calculation.
85 | *
86 | * @param valueSuppliers the suppliers of value to be calculated
87 | * @return {@code true} if any value in values is {@code true}, otherwise {@code false}
88 | */
89 | public static boolean or(BooleanSupplier... valueSuppliers) {
90 | return Arrays.stream(valueSuppliers)
91 | .filter(Objects::nonNull)
92 | .anyMatch(BooleanSupplier::getAsBoolean);
93 | }
94 |
95 | /**
96 | * Private constructor to prevent instantiation of this utility class.
97 | */
98 | private BoolUtil() {}
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/devkit-utils/src/main/java/com/onixbyte/devkit/utils/CollectionUtil.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.devkit.utils;
19 |
20 | import java.util.ArrayList;
21 | import java.util.Collection;
22 | import java.util.List;
23 | import java.util.Objects;
24 | import java.util.function.Supplier;
25 |
26 | /**
27 | * A utility class providing static methods for manipulating collections.
28 | *
29 | * @author zihluwang
30 | */
31 | public final class CollectionUtil {
32 |
33 | /**
34 | * Private constructor to prevent instantiation of this utility class.
35 | */
36 | private CollectionUtil() {
37 | }
38 |
39 | /**
40 | * Splits a collection into a list of sub-collections, each with a maximum size specified by
41 | * the caller.
42 | *
43 | * This method takes an original collection and divides it into smaller sub-collections,
44 | * ensuring that each sub-collection contains no more than the specified maximum size. If the
45 | * original collection's size is less than or equal to the maximum size, it is returned as a
46 | * single sub-collection. The sub-collections are created using the provided collection factory.
47 | *
48 | * @param the type of elements in the collection
49 | * @param the type of the collection, which must extend {@link Collection}
50 | * @param originalCollection the collection to be split into sub-collections
51 | * @param maxSize the maximum number of elements allowed in each sub-collection
52 | * @param collectionFactory a supplier that creates new instances of the sub-collection type
53 | * @return a list of sub-collections, each containing up to {@code maxSize} elements
54 | * @throws IllegalArgumentException if {@code originalCollection} is {@code null},
55 | * {@code maxSize} is less than zero, or
56 | * {@code collectionFactory} is {@code null}
57 | */
58 | public static > List chunk(C originalCollection,
59 | int maxSize,
60 | Supplier collectionFactory) {
61 | // check inputs
62 | if (Objects.isNull(originalCollection)) {
63 | throw new IllegalArgumentException("Collection must not be null.");
64 | }
65 |
66 | if (maxSize <= 0) {
67 | throw new IllegalArgumentException("maxSize must greater than 0.");
68 | }
69 |
70 | if (Objects.isNull(collectionFactory)) {
71 | throw new IllegalArgumentException("Factory method cannot be null.");
72 | }
73 |
74 | var result = new ArrayList();
75 | var size = originalCollection.size();
76 |
77 | // if original collection is empty or the size less than maxSize, return it as a single
78 | // sub collection
79 | if (size <= maxSize) {
80 | var singleCollection = collectionFactory.get();
81 | singleCollection.addAll(originalCollection);
82 | result.add(singleCollection);
83 | return result;
84 | }
85 |
86 | // use iterator to split the given collection
87 | var iter = originalCollection.iterator();
88 | var count = 0;
89 | var currentSubCollection = collectionFactory.get();
90 |
91 | while (iter.hasNext()) {
92 | var element = iter.next();
93 | currentSubCollection.add(element);
94 | count++;
95 |
96 | // add sub collection to result when current sub collection reached maxSize or
97 | // collection traverse is completed
98 | if (count % maxSize == 0 || !iter.hasNext()) {
99 | result.add(currentSubCollection);
100 | currentSubCollection = collectionFactory.get();
101 | }
102 | }
103 |
104 | return result;
105 | }
106 |
107 | }
108 |
--------------------------------------------------------------------------------
/devkit-utils/src/main/java/com/onixbyte/devkit/utils/MapUtil.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.devkit.utils;
19 |
20 | import java.util.Map;
21 |
22 | /**
23 | * The {@link MapUtil} class provides utility methods for converting between objects and maps.
24 | * This class leverages the {@link ObjectMapAdapter} interface to perform the conversions.
25 | *
26 | * The utility methods in this class are useful for scenarios where objects need to be represented
27 | * as maps for serialization, deserialization, or other purposes.
28 | *
29 | *
30 | * Example usage:
31 | *
32 | * {@code
33 | * public class User {
34 | * private String name;
35 | * private int age;
36 | *
37 | * // getters and setters
38 | * }
39 | *
40 | * public class UserMapAdapter implements ObjectMapAdapter {
41 | * @Override
42 | * public Map toMap(User user) {
43 | * Map map = new HashMap<>();
44 | * map.put("name", user.getName());
45 | * map.put("age", user.getAge());
46 | * return map;
47 | * }
48 | *
49 | * @Override
50 | * public User fromMap(Map map) {
51 | * User user = new User();
52 | * user.setName((String) map.get("name"));
53 | * user.setAge((Integer) map.get("age"));
54 | * return user;
55 | * }
56 | * }
57 | *
58 | * public class Example {
59 | * public static void main(String[] args) {
60 | * User user = new User();
61 | * user.setName("John");
62 | * user.setAge(30);
63 | *
64 | * UserMapAdapter adapter = new UserMapAdapter();
65 | *
66 | * // Convert object to map
67 | * Map userMap = MapUtil.objectToMap(user, adapter);
68 | * System.out.println(userMap); // Output: {name=John, age=30}
69 | *
70 | * // Convert map to object
71 | * User newUser = MapUtil.mapToObject(userMap, adapter);
72 | * System.out.println(newUser.getName()); // Output: John
73 | * System.out.println(newUser.getAge()); // Output: 30
74 | * }
75 | * }
76 | * }
77 | *
78 | *
79 | * @author zihluwang
80 | * @version 1.7.0
81 | * @since 1.0.0
82 | */
83 | public final class MapUtil {
84 |
85 | /**
86 | * Converts an object to a map by mapping the field names to their corresponding values.
87 | *
88 | * @param the type of the object
89 | * @param entity the object to be converted to a map
90 | * @param adapter adapts the entity for mapping to a map
91 | * @return a map representing the fields and their values of the object
92 | */
93 | public static Map objectToMap(T entity, ObjectMapAdapter adapter) {
94 | return adapter.toMap(entity);
95 | }
96 |
97 | /**
98 | * Converts a map to an object of the specified type by setting the field values using the
99 | * map entries.
100 | *
101 | * @param objectMap the map representing the fields and their values
102 | * @param adapter the adapter to execute the setter for the entity
103 | * @param the type of the object to be created
104 | * @return an object of the specified type with the field values set from the map
105 | */
106 | public static T mapToObject(Map objectMap, ObjectMapAdapter adapter) {
107 | return adapter.toObject(objectMap);
108 | }
109 |
110 | /**
111 | * Private constructor to prevent instantiation of this utility class.
112 | */
113 | private MapUtil() {
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/devkit-utils/src/main/java/com/onixbyte/devkit/utils/ObjectMapAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.devkit.utils;
19 |
20 | import java.util.Map;
21 |
22 | /**
23 | * The {@link ObjectMapAdapter} interface provides methods to convert between objects and maps.
24 | * This interface is useful for scenarios where objects need to be represented as maps for
25 | * serialization, deserialization, or other purposes.
26 | *
27 | * Implementations of this interface should provide the logic to convert an object of type
28 | * {@code T} to a {@link Map} and vice versa.
29 | *
30 | * Example usage:
31 | *
32 | * {@code
33 | * public class User {
34 | * private String name;
35 | * private int age;
36 | *
37 | * // getters and setters
38 | * }
39 | *
40 | * public class UserMapAdapter implements ObjectMapAdapter {
41 | * @Override
42 | * public Map toMap(User user) {
43 | * Map map = new HashMap<>();
44 | * map.put("name", user.getName());
45 | * map.put("age", user.getAge());
46 | * return map;
47 | * }
48 | *
49 | * @Override
50 | * public User fromMap(Map map) {
51 | * User user = new User();
52 | * user.setName((String) map.get("name"));
53 | * user.setAge((Integer) map.get("age"));
54 | * return user;
55 | * }
56 | * }
57 | * }
58 | *
59 | *
60 | * @param the type of the object to be converted
61 | * @author zihluwang
62 | * @version 1.7.0
63 | * @since 1.4.2
64 | */
65 | public interface ObjectMapAdapter {
66 |
67 | /**
68 | * Convert an object to a map.
69 | *
70 | * @param element the element that will be converted to Map
71 | * @return a Map that is converted from the element
72 | */
73 | Map toMap(T element);
74 |
75 | /**
76 | * Convert a Map to an object.
77 | *
78 | * @param map the map that will be converted to an object
79 | * @return the object that is converted from the Map
80 | */
81 | T toObject(Map map);
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/devkit-utils/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
22 |
24 |
25 |
26 |
27 |
28 | ${COLOURFUL_OUTPUT}
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/devkit-utils/src/test/java/com/onixbyte/devkit/utils/AesUtilTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.devkit.utils;
19 |
20 | import org.junit.jupiter.api.Test;
21 |
22 | import java.nio.charset.StandardCharsets;
23 | import java.security.GeneralSecurityException;
24 |
25 | import static org.junit.jupiter.api.Assertions.*;
26 |
27 | class AesUtilTest {
28 |
29 | @Test
30 | void testEncryptAndDecryptByte() throws GeneralSecurityException {
31 | byte[] secretKey = "43f72073956d4c81".getBytes(StandardCharsets.UTF_8);
32 | byte[] originalData = "Hello World".getBytes(StandardCharsets.UTF_8);
33 |
34 | byte[] encryptedData = AesUtil.encrypt(originalData, secretKey);
35 | assertNotNull(encryptedData);
36 |
37 | byte[] decryptedData = AesUtil.decrypt(encryptedData, secretKey);
38 | assertNotNull(decryptedData);
39 |
40 | assertArrayEquals(originalData, decryptedData);
41 | }
42 |
43 | @Test
44 | void testEncryptAndDecryptString() throws GeneralSecurityException {
45 | var secret = "43f72073956d4c81";
46 | var originalData = "Hello World";
47 |
48 | var encryptedData = AesUtil.encrypt(originalData, secret);
49 | assertNotNull(encryptedData);
50 | assertNotEquals(originalData, encryptedData);
51 |
52 | var decryptedData = AesUtil.decrypt(encryptedData, secret);
53 | assertNotNull(decryptedData);
54 | assertEquals(originalData, decryptedData);
55 | }
56 |
57 | @Test
58 | void testEncryptWithWrongKeyFails() throws GeneralSecurityException {
59 | var secret = "43f72073956d4c81";
60 | var wrongSecret = "0000000000000000";
61 | var originalData = "Hello World";
62 |
63 | var encryptedData = AesUtil.encrypt(originalData.getBytes(StandardCharsets.UTF_8),
64 | secret.getBytes(StandardCharsets.UTF_8));
65 | assertNotNull(encryptedData);
66 |
67 | // When decrypting with the wrong key, a BadPaddingException or IllegalBlockSizeException is expected to be thrown
68 | assertThrows(GeneralSecurityException.class, () -> {
69 | AesUtil.decrypt(encryptedData, wrongSecret.getBytes(StandardCharsets.UTF_8));
70 | });
71 | }
72 |
73 | @Test
74 | void testGenerateRandomSecret() {
75 | var randomSecret = AesUtil.generateRandomSecret();
76 | assertNotNull(randomSecret);
77 | assertEquals(16, randomSecret.length());
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/devkit-utils/src/test/java/com/onixbyte/devkit/utils/Base64UtilTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.devkit.utils;
19 |
20 | import org.junit.jupiter.api.Assertions;
21 | import org.junit.jupiter.api.Test;
22 | import org.slf4j.Logger;
23 | import org.slf4j.LoggerFactory;
24 |
25 | import java.nio.charset.StandardCharsets;
26 |
27 | import static org.junit.jupiter.api.Assertions.*;
28 |
29 | public class Base64UtilTest {
30 |
31 | @Test
32 | void testEncodeAndDecodeWithUtf8() {
33 | var original = "Hello, Base64!";
34 | var encoded = Base64Util.encode(original);
35 | assertNotNull(encoded);
36 | assertNotEquals(original, encoded);
37 |
38 | var decoded = Base64Util.decode(encoded);
39 | assertNotNull(decoded);
40 | assertEquals(original, decoded);
41 | }
42 |
43 | @Test
44 | void testEncodeAndDecodeWithCharset() {
45 | var original = "编码测试"; // Some unicode characters (Chinese)
46 | var charset = StandardCharsets.UTF_8;
47 |
48 | var encoded = Base64Util.encode(original, charset);
49 | assertNotNull(encoded);
50 | assertNotEquals(original, encoded);
51 |
52 | var decoded = Base64Util.decode(encoded, charset);
53 | assertNotNull(decoded);
54 | assertEquals(original, decoded);
55 | }
56 |
57 | @Test
58 | void testEncodeUrlComponentsAndDecodeWithUtf8() {
59 | var original = "This is a test for URL-safe Base64 encoding+!";
60 |
61 | var encodedUrl = Base64Util.encodeUrlComponents(original);
62 | assertNotNull(encodedUrl);
63 | assertNotEquals(original, encodedUrl);
64 | // URL-safe encoding should not contain '+' or '/' characters
65 | assertFalse(encodedUrl.contains("+"));
66 | assertFalse(encodedUrl.contains("/"));
67 |
68 | var decodedUrl = Base64Util.decodeUrlComponents(encodedUrl);
69 | assertNotNull(decodedUrl);
70 | assertEquals(original, decodedUrl);
71 | }
72 |
73 | @Test
74 | void testEncodeUrlComponentsAndDecodeWithCharset() {
75 | var original = "测试 URL 安全编码"; // Unicode string
76 | var charset = StandardCharsets.UTF_8;
77 |
78 | var encodedUrl = Base64Util.encodeUrlComponents(original, charset);
79 | assertNotNull(encodedUrl);
80 | assertNotEquals(original, encodedUrl);
81 |
82 | var decodedUrl = Base64Util.decodeUrlComponents(encodedUrl, charset);
83 | assertNotNull(decodedUrl);
84 | assertEquals(original, decodedUrl);
85 | }
86 |
87 | @Test
88 | void testEncodeAndDecodeEmptyString() {
89 | var original = "";
90 |
91 | var encoded = Base64Util.encode(original);
92 | assertNotNull(encoded);
93 | assertEquals("", Base64Util.decode(encoded));
94 | }
95 |
96 | @Test
97 | void testEncodeAndDecodeNullSafety() {
98 | // Since Base64Util does not explicitly handle null, the test expects NPE if null is input
99 | assertThrows(NullPointerException.class, () -> Base64Util.encode(null));
100 | assertThrows(NullPointerException.class, () -> Base64Util.decode(null));
101 | }
102 |
103 | }
104 |
--------------------------------------------------------------------------------
/devkit-utils/src/test/java/com/onixbyte/devkit/utils/BoolUtilTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.devkit.utils;
19 |
20 | import org.junit.jupiter.api.Test;
21 |
22 | import java.util.function.BooleanSupplier;
23 |
24 | import static org.junit.jupiter.api.Assertions.assertFalse;
25 | import static org.junit.jupiter.api.Assertions.assertTrue;
26 |
27 | public class BoolUtilTest {
28 |
29 | // Tests for and(Boolean... values)
30 |
31 | @Test
32 | void and_AllTrueValues_ReturnsTrue() {
33 | assertTrue(BoolUtil.and(true, true, true));
34 | }
35 |
36 | @Test
37 | void and_SomeFalseValues_ReturnsFalse() {
38 | assertFalse(BoolUtil.and(true, false, true));
39 | }
40 |
41 | @Test
42 | void and_AllFalseValues_ReturnsFalse() {
43 | assertFalse(BoolUtil.and(false, false));
44 | }
45 |
46 | @Test
47 | void and_WithNullValues_IgnoresNulls() {
48 | assertTrue(BoolUtil.and(true, null, true));
49 | assertFalse(BoolUtil.and(true, null, false));
50 | }
51 |
52 | @Test
53 | void and_AllNullValues_ReturnsTrue() {
54 | // Stream after filtering null is empty, allMatch on empty returns true
55 | assertTrue(BoolUtil.and((Boolean) null, null));
56 | }
57 |
58 | // Tests for and(BooleanSupplier... valueSuppliers)
59 |
60 | @Test
61 | void and_AllSuppliersTrue_ReturnsTrue() {
62 | BooleanSupplier trueSupplier = () -> true;
63 | BooleanSupplier falseSupplier = () -> false;
64 |
65 | assertTrue(BoolUtil.and(trueSupplier, trueSupplier));
66 | assertFalse(BoolUtil.and(trueSupplier, falseSupplier));
67 | }
68 |
69 | @Test
70 | void and_WithNullSuppliers_IgnoresNull() {
71 | BooleanSupplier trueSupplier = () -> true;
72 |
73 | assertTrue(BoolUtil.and(trueSupplier, null, trueSupplier));
74 | assertFalse(BoolUtil.and(trueSupplier, null, () -> false));
75 | }
76 |
77 | @Test
78 | void and_AllNullSuppliers_ReturnsTrue() {
79 | assertTrue(BoolUtil.and((BooleanSupplier) null, null));
80 | }
81 |
82 |
83 | // Tests for or(Boolean... values)
84 |
85 | @Test
86 | void or_AllTrueValues_ReturnsTrue() {
87 | assertTrue(BoolUtil.or(true, true, true));
88 | }
89 |
90 | @Test
91 | void or_SomeTrueValues_ReturnsTrue() {
92 | assertTrue(BoolUtil.or(false, true, false));
93 | }
94 |
95 | @Test
96 | void or_AllFalseValues_ReturnsFalse() {
97 | assertFalse(BoolUtil.or(false, false));
98 | }
99 |
100 | @Test
101 | void or_WithNullValues_IgnoresNull() {
102 | assertTrue(BoolUtil.or(false, null, true));
103 | assertFalse(BoolUtil.or(false, null, false));
104 | }
105 |
106 | @Test
107 | void or_AllNullValues_ReturnsFalse() {
108 | // Stream after filtering null is empty, anyMatch on empty returns false
109 | assertFalse(BoolUtil.or((Boolean) null, null));
110 | }
111 |
112 | // Tests for or(BooleanSupplier... valueSuppliers)
113 |
114 | @Test
115 | void or_AllSuppliersTrue_ReturnsTrue() {
116 | BooleanSupplier trueSupplier = () -> true;
117 | BooleanSupplier falseSupplier = () -> false;
118 |
119 | assertTrue(BoolUtil.or(trueSupplier, trueSupplier));
120 | assertTrue(BoolUtil.or(falseSupplier, trueSupplier));
121 | assertFalse(BoolUtil.or(falseSupplier, falseSupplier));
122 | }
123 |
124 | @Test
125 | void or_WithNullSuppliers_IgnoresNull() {
126 | BooleanSupplier trueSupplier = () -> true;
127 | BooleanSupplier falseSupplier = () -> false;
128 |
129 | assertTrue(BoolUtil.or(falseSupplier, null, trueSupplier));
130 | assertFalse(BoolUtil.or(falseSupplier, null, falseSupplier));
131 | }
132 |
133 | @Test
134 | void or_AllNullSuppliers_ReturnsFalse() {
135 | assertFalse(BoolUtil.or((BooleanSupplier) null, null));
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/devkit-utils/src/test/java/com/onixbyte/devkit/utils/CollectionUtilTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.devkit.utils;
19 |
20 | import org.junit.jupiter.api.Test;
21 |
22 | import java.util.*;
23 | import java.util.function.Supplier;
24 |
25 | import static org.junit.jupiter.api.Assertions.*;
26 |
27 | class CollectionUtilTest {
28 |
29 | @Test
30 | void chunk_NullOriginalCollection_ThrowsException() {
31 | IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
32 | () -> CollectionUtil.chunk(null, 3, ArrayList::new));
33 | assertEquals("Collection must not be null.", ex.getMessage());
34 | }
35 |
36 | @Test
37 | void chunk_NegativeMaxSize_ThrowsException() {
38 | IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
39 | () -> CollectionUtil.chunk(List.of(1, 2), -1, ArrayList::new));
40 | assertEquals("maxSize must greater than 0.", ex.getMessage());
41 | }
42 |
43 | @Test
44 | void chunk_NullCollectionFactory_ThrowsException() {
45 | IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
46 | () -> CollectionUtil.chunk(List.of(1, 2), 2, null));
47 | assertEquals("Factory method cannot be null.", ex.getMessage());
48 | }
49 |
50 | @Test
51 | void chunk_EmptyCollection_ReturnsOneEmptySubCollection() {
52 | List> chunks = CollectionUtil.chunk(Collections.emptyList(), 3, ArrayList::new);
53 | assertEquals(1, chunks.size());
54 | assertTrue(chunks.get(0).isEmpty());
55 | }
56 |
57 | @Test
58 | void chunk_CollectionSizeLessThanMaxSize_ReturnsOneSubCollectionWithAllElements() {
59 | List list = List.of(1, 2);
60 | List> chunks = CollectionUtil.chunk(list, 5, ArrayList::new);
61 | assertEquals(1, chunks.size());
62 | assertEquals(list, chunks.get(0));
63 | }
64 |
65 | @Test
66 | void chunk_CollectionSizeEqualMaxSize_ReturnsOneSubCollectionWithAllElements() {
67 | List list = List.of(1, 2, 3);
68 | List> chunks = CollectionUtil.chunk(list, 3, ArrayList::new);
69 | assertEquals(1, chunks.size());
70 | assertEquals(list, chunks.get(0));
71 | }
72 |
73 | @Test
74 | void chunk_CollectionSizeGreaterThanMaxSize_ReturnsMultipleSubCollections() {
75 | List list = List.of(1, 2, 3, 4, 5, 6, 7);
76 | int maxSize = 3;
77 | List> chunks = CollectionUtil.chunk(list, maxSize, ArrayList::new);
78 |
79 | // Expect 3 subcollections: [1,2,3], [4,5,6], [7]
80 | assertEquals(3, chunks.size());
81 | assertEquals(List.of(1, 2, 3), chunks.get(0));
82 | assertEquals(List.of(4, 5, 6), chunks.get(1));
83 | assertEquals(List.of(7), chunks.get(2));
84 | }
85 |
86 | @Test
87 | void chunk_UsesDifferentCollectionTypeAsSubCollections() {
88 | LinkedList list = new LinkedList<>(List.of(1, 2, 3, 4));
89 | Supplier> factory = LinkedList::new;
90 | List> chunks = CollectionUtil.chunk(list, 2, factory);
91 | assertEquals(2, chunks.size());
92 | assertInstanceOf(LinkedList.class, chunks.get(0));
93 | assertInstanceOf(LinkedList.class, chunks.get(1));
94 | assertEquals(List.of(1, 2), chunks.get(0));
95 | assertEquals(List.of(3, 4), chunks.get(1));
96 | }
97 |
98 | @Test
99 | void chunk_CollectionWithOneElementAndMaxSizeOne_ReturnsOneSubCollection() {
100 | List list = List.of("a");
101 | List> chunks = CollectionUtil.chunk(list, 1, ArrayList::new);
102 | assertEquals(1, chunks.size());
103 | assertEquals(list, chunks.get(0));
104 | }
105 |
106 | @Test
107 | void chunk_MaxSizeZero_ThrowsException() {
108 | IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
109 | () -> CollectionUtil.chunk(List.of(1), 0, ArrayList::new));
110 | assertEquals("maxSize must greater than 0.", ex.getMessage());
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/devkit-utils/src/test/java/com/onixbyte/devkit/utils/RangeUtilTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.devkit.utils;
19 |
20 | import org.junit.jupiter.api.Test;
21 |
22 | import static org.junit.jupiter.api.Assertions.*;
23 |
24 | class RangeUtilTest {
25 |
26 | /**
27 | * Tests generating ascending range from 0 up to end (exclusive).
28 | */
29 | @Test
30 | void testRangeEndValid() {
31 | int[] expected = {0, 1, 2, 3, 4};
32 | assertArrayEquals(expected, RangeUtil.range(5).toArray());
33 | }
34 |
35 | /**
36 | * Tests that range(end) throws IllegalArgumentException for end less than or equal to zero.
37 | */
38 | @Test
39 | void testRangeEndInvalidThrows() {
40 | IllegalArgumentException ex1 = assertThrows(IllegalArgumentException.class,
41 | () -> RangeUtil.range(0));
42 | assertTrue(ex1.getMessage().contains("should not be less than or equal to 0"));
43 |
44 | IllegalArgumentException ex2 = assertThrows(IllegalArgumentException.class,
45 | () -> RangeUtil.range(-3));
46 | assertTrue(ex2.getMessage().contains("should not be less than or equal to 0"));
47 | }
48 |
49 | /**
50 | * Tests ascending range where start is less than end.
51 | */
52 | @Test
53 | void testRangeStartEndAscending() {
54 | int[] expected = {3, 4, 5, 6, 7};
55 | assertArrayEquals(expected, RangeUtil.range(3, 8).toArray());
56 | }
57 |
58 | /**
59 | * Tests descending range where start is greater than end.
60 | */
61 | @Test
62 | void testRangeStartEndDescending() {
63 | int[] expected = {8, 7, 6, 5, 4};
64 | assertArrayEquals(expected, RangeUtil.range(8, 3).toArray());
65 | }
66 |
67 | /**
68 | * Tests empty stream when start equals end.
69 | */
70 | @Test
71 | void testRangeStartEqualsEndReturnsEmpty() {
72 | assertEquals(0, RangeUtil.range(5, 5).count());
73 | }
74 |
75 | /**
76 | * Tests that rangeClosed generates inclusive range in ascending order.
77 | */
78 | @Test
79 | void testRangeClosedAscending() {
80 | int[] expected = {3, 4, 5, 6, 7, 8};
81 | assertArrayEquals(expected, RangeUtil.rangeClosed(3, 8).toArray());
82 | }
83 |
84 | /**
85 | * Tests range method with positive step generating ascending sequence.
86 | */
87 | @Test
88 | void testRangeWithPositiveStep() {
89 | int[] expected = {2, 4, 6, 8};
90 | assertArrayEquals(expected, RangeUtil.range(2, 10, 2).toArray());
91 | }
92 |
93 | /**
94 | * Tests range method with negative step generating descending sequence.
95 | */
96 | @Test
97 | void testRangeWithNegativeStep() {
98 | int[] expected = {10, 7, 4, 1};
99 | assertArrayEquals(expected, RangeUtil.range(10, 0, -3).toArray());
100 | }
101 |
102 | /**
103 | * Tests that passing zero step throws IllegalArgumentException.
104 | */
105 | @Test
106 | void testRangeStepZeroThrows() {
107 | IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
108 | () -> RangeUtil.range(0, 10, 0));
109 | assertEquals("Step value must not be zero.", ex.getMessage());
110 | }
111 |
112 | /**
113 | * Tests that range with positive step but invalid start/end throws IllegalArgumentException.
114 | */
115 | @Test
116 | void testRangePositiveStepInvalidRangeThrows() {
117 | IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
118 | () -> RangeUtil.range(10, 5, 1));
119 | assertEquals("Range parameters are inconsistent with the step value.", ex.getMessage());
120 | }
121 |
122 | /**
123 | * Tests that range with negative step but invalid start/end throws IllegalArgumentException.
124 | */
125 | @Test
126 | void testRangeNegativeStepInvalidRangeThrows() {
127 | IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
128 | () -> RangeUtil.range(5, 10, -1));
129 | assertEquals("Range parameters are inconsistent with the step value.", ex.getMessage());
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright (C) 2024-2025 OnixByte.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | #
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 | #
17 |
18 | artefactVersion=2.2.0
19 | projectUrl=https://onixbyte.com/java-dev-kit
20 | projectGithubUrl=https://github.com/onixbyte/java-dev-kit
21 | licenseName=The Apache License, Version 2.0
22 | licenseUrl=https://www.apache.org/licenses/LICENSE-2.0.txt
--------------------------------------------------------------------------------
/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2024-2025 OnixByte.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | #
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | [versions]
17 | slf4j = "2.0.17"
18 | logback = "1.5.18"
19 | jackson = "2.18.4"
20 | jwt = "4.5.0"
21 | spring = "6.2.6"
22 | springBoot = "3.4.5"
23 | junit = "5.11.4"
24 |
25 | [libraries]
26 | slf4j = { group = "org.slf4j", name = "slf4j-api", version.ref = "slf4j" }
27 | logback = { group = "ch.qos.logback", name = "logback-classic", version.ref = "logback" }
28 | jackson-core = { group = "com.fasterxml.jackson.core", name = "jackson-core", version.ref = "jackson" }
29 | jackson-databind = { group = "com.fasterxml.jackson.core", name = "jackson-databind", version.ref = "jackson" }
30 | jwt-core = { group = "com.auth0", name = "java-jwt", version.ref = "jwt" }
31 | springBoot-autoconfigure = { group = "org.springframework.boot", name = "spring-boot-autoconfigure", version.ref = "springBoot" }
32 | springBoot-starter-logging = { group = "org.springframework.boot", name = "spring-boot-starter-logging", version.ref = "springBoot" }
33 | springBoot-configurationProcessor = { group = "org.springframework.boot", name = "spring-boot-configuration-processor", version.ref = "springBoot" }
34 | springBoot-starter-redis = { group = "org.springframework.boot", name = "spring-boot-starter-data-redis", version.ref = "springBoot" }
35 | springBoot-starter-test = { group = "org.springframework.boot", name = "spring-boot-starter-test", version.ref = "springBoot" }
36 | junit-bom = { group = "org.junit", name = "junit-bom", version.ref = "junit" }
37 | junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version.ref = "junit" }
38 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/onixbyte/java-dev-kit/744f82f2e3d0a81f3ca0377b219fa03a26edf223/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright (C) 2024-2025 OnixByte.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | #
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 | #
17 |
18 | distributionBase=GRADLE_USER_HOME
19 | distributionPath=wrapper/dists
20 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
21 | networkTimeout=10000
22 | validateDistributionUrl=true
23 | zipStoreBase=GRADLE_USER_HOME
24 | zipStorePath=wrapper/dists
25 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | @rem This is normally unused
30 | set APP_BASE_NAME=%~n0
31 | set APP_HOME=%DIRNAME%
32 |
33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35 |
36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
38 |
39 | @rem Find java.exe
40 | if defined JAVA_HOME goto findJavaFromJavaHome
41 |
42 | set JAVA_EXE=java.exe
43 | %JAVA_EXE% -version >NUL 2>&1
44 | if %ERRORLEVEL% equ 0 goto execute
45 |
46 | echo. 1>&2
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
48 | echo. 1>&2
49 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
50 | echo location of your Java installation. 1>&2
51 |
52 | goto fail
53 |
54 | :findJavaFromJavaHome
55 | set JAVA_HOME=%JAVA_HOME:"=%
56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 |
58 | if exist "%JAVA_EXE%" goto execute
59 |
60 | echo. 1>&2
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
62 | echo. 1>&2
63 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
64 | echo location of your Java installation. 1>&2
65 |
66 | goto fail
67 |
68 | :execute
69 | @rem Setup the command line
70 |
71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72 |
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if %ERRORLEVEL% equ 0 goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | set EXIT_CODE=%ERRORLEVEL%
85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
87 | exit /b %EXIT_CODE%
88 |
89 | :mainEnd
90 | if "%OS%"=="Windows_NT" endlocal
91 |
92 | :omega
93 |
--------------------------------------------------------------------------------
/guid/README.md:
--------------------------------------------------------------------------------
1 | # Module `guid`
2 |
3 | ## Introduction
4 |
5 | Module `guid` serves as a guid creator for other `JDevKit` modules. You can also use this module as a guid creator standards.
6 |
7 | We have already implemented `SnowflakeGuidCreator`, you can also implement a custom guid creations by implementing `com.onixbyte.guid.GuidCreator`.
8 |
9 | ## Example usage
10 |
11 | ### A UUID creator
12 |
13 | ```java
14 | GuidCreator uuidCreator = (GuidCreator) UUID::randomUUID;
15 | ```
16 |
17 | ### A custom guid creator
18 |
19 | Assume that you need serial guid creator.
20 |
21 | ```java
22 | @Component
23 | public class CustomGuidCreator implementes GuidCreator {
24 |
25 | public final RedisTemplate serialRedisTemplate;
26 |
27 | @Autowired
28 | public CustomGuidCreator(RedisTemplate serialRedisTemplate) {
29 | this.serialRedisTemplate = serialRedisTemplate;
30 | }
31 |
32 | @Override public String nextId() {
33 | return "SOME_PREFIX" + serialRedisTemplate.opsForValue().get("some_serial_key");
34 | }
35 |
36 | }
37 | ```
38 |
39 |
--------------------------------------------------------------------------------
/guid/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | import java.net.URI
18 |
19 | plugins {
20 | java
21 | id("java-library")
22 | id("maven-publish")
23 | id("signing")
24 | }
25 |
26 | val artefactVersion: String by project
27 | val projectUrl: String by project
28 | val projectGithubUrl: String by project
29 | val licenseName: String by project
30 | val licenseUrl: String by project
31 |
32 | group = "com.onixbyte"
33 | version = artefactVersion
34 |
35 | repositories {
36 | mavenCentral()
37 | }
38 |
39 | java {
40 | sourceCompatibility = JavaVersion.VERSION_17
41 | targetCompatibility = JavaVersion.VERSION_17
42 | withSourcesJar()
43 | withJavadocJar()
44 | }
45 |
46 | tasks.withType {
47 | options.encoding = "UTF-8"
48 | }
49 |
50 | tasks.withType {
51 | exclude("logback.xml")
52 | }
53 |
54 | dependencies {
55 | compileOnly(libs.slf4j)
56 | implementation(libs.logback)
57 | api(project(":devkit-core"))
58 | testImplementation(platform(libs.junit.bom))
59 | testImplementation(libs.junit.jupiter)
60 | }
61 |
62 | tasks.test {
63 | useJUnitPlatform()
64 | }
65 |
66 | publishing {
67 | publications {
68 | create("guid") {
69 | groupId = group.toString()
70 | artifactId = "guid"
71 | version = artefactVersion
72 |
73 | pom {
74 | name = "DevKit - GUID"
75 | description = "The module for generating GUIDs of JDevKit."
76 | url = projectUrl
77 |
78 | licenses {
79 | license {
80 | name = licenseName
81 | url = licenseUrl
82 | }
83 | }
84 |
85 | scm {
86 | connection = "scm:git:git://github.com:OnixByte/JDevKit.git"
87 | developerConnection = "scm:git:git://github.com:OnixByte/JDevKit.git"
88 | url = projectGithubUrl
89 | }
90 |
91 | developers {
92 | developer {
93 | id = "zihluwang"
94 | name = "Zihlu Wang"
95 | email = "really@zihlu.wang"
96 | timezone = "Asia/Hong_Kong"
97 | }
98 |
99 | developer {
100 | id = "siujamo"
101 | name = "Siu Jam'o"
102 | email = "jamo.siu@outlook.com"
103 | timezone = "Asia/Shanghai"
104 | }
105 | }
106 | }
107 |
108 | from(components["java"])
109 |
110 | signing {
111 | sign(publishing.publications["guid"])
112 | }
113 | }
114 |
115 | repositories {
116 | maven {
117 | name = "sonatypeNexus"
118 | url = URI(providers.gradleProperty("repo.maven-central.host").get())
119 | credentials {
120 | username = providers.gradleProperty("repo.maven-central.username").get()
121 | password = providers.gradleProperty("repo.maven-central.password").get()
122 | }
123 | }
124 | }
125 | }
126 | }
--------------------------------------------------------------------------------
/guid/src/main/java/com/onixbyte/guid/GuidCreator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.guid;
19 |
20 | /**
21 | * The {@code GuidCreator} is a generic interface for generating globally unique identifiers (GUIDs)
22 | * of a specific type.
23 | *
24 | * The type of ID is determined by the class implementing this interface.
25 | *
26 | *
27 | * Example usage:
28 | * {@code
29 | * public class StringGuidCreator implements GuidCreator {
30 | * private final AtomicLong counter = new AtomicLong();
31 | *
32 | * @Override
33 | * public String nextId() {
34 | * return UUID.randomUUID().toString() + "-" + counter.incrementAndGet();
35 | * }
36 | * }
37 | *
38 | * public class Example {
39 | * public static void main(String[] args) {
40 | * GuidCreator guidCreator = new StringGuidCreator();
41 | * String guid = guidCreator.nextId();
42 | * System.out.println("Generated GUID: " + guid);
43 | * }
44 | * }
45 | * }
46 | *
47 | * @param this represents the type of the Global Unique Identifier
48 | * @author Zihlu Wang
49 | * @version 1.1.0
50 | * @since 1.0.0
51 | */
52 | public interface GuidCreator {
53 |
54 | /**
55 | * Generates and returns the next globally unique ID.
56 | *
57 | * @return the next globally unique ID
58 | */
59 | IdType nextId();
60 |
61 | }
--------------------------------------------------------------------------------
/guid/src/main/java/com/onixbyte/guid/exceptions/TimingException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.guid.exceptions;
19 |
20 | /**
21 | * The {@code TimingException} class represents an exception that is thrown when there is an error
22 | * related to time sequence.
23 | *
24 | * Instances of TimingException can be created with or without a message and a cause. The message
25 | * provides a description of the exception, while the cause represents the underlying cause of the
26 | * exception and provides additional information about the error.
27 | *
28 | * @author Zihlu Wang
29 | * @since 1.0.0
30 | */
31 | public class TimingException extends RuntimeException {
32 |
33 | /**
34 | * A custom exception that is thrown when there is an issue with timing or scheduling.
35 | */
36 | public TimingException() {
37 | }
38 |
39 | /**
40 | * A custom exception that is thrown when there is an issue with timing or scheduling with
41 | * customised error message.
42 | *
43 | * @param message customised message
44 | */
45 | public TimingException(String message) {
46 | super(message);
47 | }
48 |
49 | /**
50 | * A custom exception that is thrown when there is an issue with timing or scheduling with
51 | * customised error message.
52 | *
53 | * @param message customised message
54 | * @param cause the cause of this exception
55 | */
56 | public TimingException(String message, Throwable cause) {
57 | super(message, cause);
58 | }
59 |
60 | /**
61 | * A custom exception that is thrown when there is an issue with timing or scheduling with
62 | * customised error message.
63 | *
64 | * @param cause the cause of this exception
65 | */
66 | public TimingException(Throwable cause) {
67 | super(cause);
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/guid/src/main/java/com/onixbyte/guid/impl/SequentialUuidCreator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.guid.impl;
19 |
20 | import com.onixbyte.guid.GuidCreator;
21 |
22 | import java.nio.ByteBuffer;
23 | import java.security.SecureRandom;
24 | import java.util.UUID;
25 |
26 | /**
27 | * A {@code SequentialUuidCreator} is responsible for generating UUIDs following the UUID version 7 specification, which
28 | * combines a timestamp with random bytes to create time-ordered unique identifiers.
29 | *
30 | * This implementation utilises a cryptographically strong {@link SecureRandom} instance to produce the random
31 | * component of the UUID. The first 6 bytes of the UUID encode the current timestamp in milliseconds, ensuring that
32 | * generated UUIDs are roughly ordered by creation time.
33 | *
34 | * The generated UUID adheres strictly to the layout and variant bits of UUID version 7 as defined in the specification.
35 | *
36 | */
37 | public class SequentialUuidCreator implements GuidCreator {
38 |
39 | private final SecureRandom random;
40 |
41 | /**
42 | * Constructs a new {@code SequentialUuidCreator} with its own {@link SecureRandom} instance.
43 | */
44 | public SequentialUuidCreator() {
45 | this.random = new SecureRandom();
46 | }
47 |
48 | /**
49 | * Generates and returns the next UUID version 7 identifier.
50 | *
51 | * @return a {@link UUID} instance representing a unique, time-ordered identifier
52 | */
53 | @Override
54 | public UUID nextId() {
55 | var value = randomBytes();
56 | var buf = ByteBuffer.wrap(value);
57 | var high = buf.getLong();
58 | var low = buf.getLong();
59 | return new UUID(high, low);
60 | }
61 |
62 | /**
63 | * Produces a byte array representation of a UUID version 7,
64 | * combining the current timestamp with random bytes.
65 | *
66 | * @return a 16-byte array conforming to UUIDv7 layout and variant bits
67 | */
68 | private byte[] randomBytes() {
69 | var value = new byte[16];
70 | random.nextBytes(value);
71 |
72 | var timestamp = ByteBuffer.allocate(Long.BYTES);
73 | timestamp.putLong(System.currentTimeMillis());
74 |
75 | System.arraycopy(timestamp.array(), 2, value, 0, 6);
76 |
77 | // Set version to 7 (UUIDv7)
78 | value[6] = (byte) ((value[6] & 0x0F) | 0x70);
79 |
80 | // Set variant bits as per RFC 4122
81 | value[8] = (byte) ((value[8] & 0x3F) | 0x80);
82 |
83 | return value;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/guid/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | ${COLOURFUL_OUTPUT}
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/key-pair-loader/README.md:
--------------------------------------------------------------------------------
1 | # KeyLoader
2 |
3 | KeyLoader provides utility methods to load keys from pem-formatted key texts.
4 |
5 | ## ECDSA-based algorithm
6 |
7 | ### Generate key pair
8 |
9 | #### Generate private key
10 |
11 | Generate a private key by `genpkey` command provided by OpenSSL:
12 |
13 | ```shell
14 | openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out ec_private_key.pem
15 | ```
16 |
17 | The output of this command is a file called `ec_private_key.pem` and its content looks like the
18 | following:
19 |
20 | ```text
21 | -----BEGIN PRIVATE KEY-----
22 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgs79JlARgXEf6EDV7
23 | +PHQCTHEMtqIoHOy1GZ1+ynQJ6yhRANCAARkA7GRY2i4gg8qx0XViAXUP9cPw9pn
24 | Jg1wfrQ41FaMyqVBejNYxvaLtamErF/ySimnjafMJ+VZCh34lBj6Ez8R
25 | -----END PRIVATE KEY-----
26 | ```
27 |
28 | #### Generate public key by private key
29 |
30 | Export public key from private key with `ec` command provided by OpenSSL:
31 |
32 | ```shell
33 | openssl ec -in ec_private_key.pem -pubout -out ec_public_key.pem
34 | ```
35 |
36 | The output of this command is a file called `ec_public_key.pem` and its content looks like the
37 | following:
38 |
39 | ```text
40 | -----BEGIN PUBLIC KEY-----
41 | MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZAOxkWNouIIPKsdF1YgF1D/XD8Pa
42 | ZyYNcH60ONRWjMqlQXozWMb2i7WphKxf8kopp42nzCflWQod+JQY+hM/EQ==
43 | -----END PUBLIC KEY-----
44 | ```
45 |
46 | ## RSA-based algorithm
47 |
48 | ### Generate key pair
49 |
50 | #### Generate private key
51 |
52 | Generate a private key by `genpkey` command provided by OpenSSL:
53 |
54 | ```shell
55 | openssl genpkey -algorithm RSA -out rsa_private_key.pem -pkeyopt rsa_keygen_bits:2048
56 | ```
57 |
58 | The output of this command is a file called `rsa_private_key.pem` and its content looks like the
59 | following:
60 |
61 | ```text
62 | -----BEGIN PRIVATE KEY-----
63 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQD4VIFYJFMAs15j
64 | J3V3IicHd7sI2TIFqTZME40zlOlVAlPKLZmTQvZFLNgaUAAsvPi5i1DR2ywwK6Al
65 | BfnwVnzvmDXC5mKHOz4oxOQVA6Nlp2yVaQMzidmfYNSkMtcv/4HRPsatc7K/M5l6
66 | pCP20DVRjkikBdIy8e9w+x6BrIFp5Q8PZc/X2BGNAUMMYACdeYH5R/A0CxqkND13
67 | esc4gkynMOrvZrZGHCz51usfSCqyDWWwsN+GG6LYWia4GkNlS0erQnP8gS93dfjl
68 | e96BIfy3z7Iv+kUrf5ikNW2P8jMxLAv6LO+dcUAu9k477wIAF7Iq5KMuH/otsDOu
69 | +h+2qXmBAgMBAAECggEAdRqcmC0g+y6arxV3fkObthjPGYAa57KBCWUa7B0n30+m
70 | pavVRS2Jpttb2SSqwG4ouI6rARti/iBEd9EWqTCP4AieKZetFOpqCJ24lPRPRGus
71 | d9S6jr5N4qut+vSCp37NABijZj4uJ540nTH0R7qtuhTnynl4Q0/1wwiYvTvVF1Lg
72 | dn+I/8aRbshwDhdAOWOUe6GL7/eaCYgN8/UmlKIpp8tg0w2iWxbaFiR7gZiM41LA
73 | M6SXXfcCas+ZVXsGbzQ3SNiVurCGuuRNcCScXS3/WoEDIb3cNtp49iOmQS+nmEoo
74 | wh4uiEd+0+BrzxngS4o5+mKnHJnwgY0+veGVYLMR5QKBgQD9WKQmevMDU5c+NPq9
75 | 8jaR457Fuxq1gwzeFNJdWfOc/K2LEWh+nFNFCb++EboEj6FdxWaWNMxbrmJps5gs
76 | EoBUYy/Tl7UycDqDfiYLmDdTsf2pVjjh9jaIADiLcJ8S6wwJMZKub7Tp8UVkenAl
77 | 535MqShLUC11Y7VxLb3Tsll4XwKBgQD67mm6iCmshr/eszPfNE3ylZ+PiNa7nat7
78 | N7lQzBIiRJflT1kmVidC5gE+jASqH728ChkZZKxbHsjxpmWdAhLOITdXoTB4sDsd
79 | wtV1lxkXxK9FnrpFvO3y1wZ/QsD3Z2KXxHYZqawkUETO9F3nqAXW0b2GDar5Qiyo
80 | J3Tx/43aHwKBgDC0NMJtCoDONhowZy/S+6iqQKC0qprQec3L5PErVMkOTnKYwyTr
81 | +pogGKt6ju9HiXcUdvdTaSIK8UJu00dNuzv94XjlBmGO78DNpJTAC4rcge5m9AKE
82 | qdEVcclkukARzbuKuy8rrHT4/CUn4J141m/4aRWpcUPLCluato6XD9ozAoGBANvf
83 | JhOFFgcPd3YazfvpZ9eE1XA+tfFlYYmxNRcgCU+vjO0oDvSxjutmgHae18N91pG6
84 | w21lskSRf/+GDwl5dKLbphOJsOA/gz07qDDGOf2CoRW+1Hcg6drcINxH0K+4DkLv
85 | qZApBSY4k2JH6zR+HMeztn6M4WBRZLHfCPC3PUN/AoGAA3AoHbLTZvqMIKSDkP4Y
86 | U/tTsSFDY4aYo7LG/jk8af3oPU3KyGh4ZFBd6aMmXbS8f8FjvmrM+/e+y9OOGAlq
87 | iOl0hYrs5cJSMLW6i4KnJYuYbMkgmk3bN2t9apu64xKR94gbPrI6AGnPZp+iIzp0
88 | hXKe4HcuhQ3G0a2hjayiQ84=
89 | -----END PRIVATE KEY-----
90 | ```
91 |
92 | #### Generate public key by private key
93 |
94 | Export public key from private key by OpenSSL:
95 |
96 | ```shell
97 | openssl pkey -in rsa_private_key.pem -pubout -out rsa_public_key.pem
98 | ```
99 |
100 | The output of this command is a file called `rsa_public_key.pem` and its content looks like the
101 | following:
102 |
103 | ```text
104 | -----BEGIN PUBLIC KEY-----
105 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+FSBWCRTALNeYyd1dyIn
106 | B3e7CNkyBak2TBONM5TpVQJTyi2Zk0L2RSzYGlAALLz4uYtQ0dssMCugJQX58FZ8
107 | 75g1wuZihzs+KMTkFQOjZadslWkDM4nZn2DUpDLXL/+B0T7GrXOyvzOZeqQj9tA1
108 | UY5IpAXSMvHvcPsegayBaeUPD2XP19gRjQFDDGAAnXmB+UfwNAsapDQ9d3rHOIJM
109 | pzDq72a2Rhws+dbrH0gqsg1lsLDfhhui2FomuBpDZUtHq0Jz/IEvd3X45XvegSH8
110 | t8+yL/pFK3+YpDVtj/IzMSwL+izvnXFALvZOO+8CABeyKuSjLh/6LbAzrvoftql5
111 | gQIDAQAB
112 | -----END PUBLIC KEY-----
113 | ```
--------------------------------------------------------------------------------
/key-pair-loader/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | import java.net.URI
18 |
19 | plugins {
20 | java
21 | id("java-library")
22 | id("maven-publish")
23 | id("signing")
24 | }
25 |
26 | val artefactVersion: String by project
27 | val projectUrl: String by project
28 | val projectGithubUrl: String by project
29 | val licenseName: String by project
30 | val licenseUrl: String by project
31 |
32 | group = "com.onixbyte"
33 | version = artefactVersion
34 |
35 | repositories {
36 | mavenCentral()
37 | }
38 |
39 | java {
40 | sourceCompatibility = JavaVersion.VERSION_17
41 | targetCompatibility = JavaVersion.VERSION_17
42 | withSourcesJar()
43 | withJavadocJar()
44 | }
45 |
46 | tasks.withType {
47 | options.encoding = "UTF-8"
48 | }
49 |
50 | tasks.withType {
51 | exclude("logback.xml")
52 | }
53 |
54 | dependencies {
55 | compileOnly(libs.slf4j)
56 | implementation(libs.logback)
57 | api(project(":devkit-core"))
58 | testImplementation(libs.jwt.core)
59 | testImplementation(platform(libs.junit.bom))
60 | testImplementation(libs.junit.jupiter)
61 | }
62 |
63 | tasks.test {
64 | useJUnitPlatform()
65 | testLogging {
66 | events("passed", "skipped", "failed")
67 | }
68 | }
69 |
70 | publishing {
71 | publications {
72 | create("keyPairLoader") {
73 | groupId = group.toString()
74 | artifactId = "key-pair-loader"
75 | version = artefactVersion
76 |
77 | pom {
78 | name = "Key Pair Loader"
79 | description =
80 | "This module can easily load key pairs from a PEM content."
81 | url = projectUrl
82 |
83 | licenses {
84 | license {
85 | name = licenseName
86 | url = licenseUrl
87 | }
88 | }
89 |
90 | scm {
91 | connection = "scm:git:git://github.com:OnixByte/JDevKit.git"
92 | developerConnection = "scm:git:git://github.com:OnixByte/JDevKit.git"
93 | url = projectGithubUrl
94 | }
95 |
96 | developers {
97 | developer {
98 | id = "zihluwang"
99 | name = "Zihlu Wang"
100 | email = "really@zihlu.wang"
101 | timezone = "Asia/Hong_Kong"
102 | }
103 |
104 | developer {
105 | id = "siujamo"
106 | name = "Siu Jam'o"
107 | email = "jamo.siu@outlook.com"
108 | timezone = "Asia/Shanghai"
109 | }
110 | }
111 | }
112 |
113 | from(components["java"])
114 |
115 | signing {
116 | sign(publishing.publications["keyPairLoader"])
117 | }
118 | }
119 |
120 | repositories {
121 | maven {
122 | name = "sonatypeNexus"
123 | url = URI(providers.gradleProperty("repo.maven-central.host").get())
124 | credentials {
125 | username = providers.gradleProperty("repo.maven-central.username").get()
126 | password = providers.gradleProperty("repo.maven-central.password").get()
127 | }
128 | }
129 | }
130 | }
131 | }
--------------------------------------------------------------------------------
/key-pair-loader/src/main/java/com/onixbyte/security/KeyLoader.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.security;
19 |
20 | import java.security.PrivateKey;
21 | import java.security.PublicKey;
22 |
23 | /**
24 | * The {@code KeyLoader} class provides utility methods for loading keys pairs from PEM-formatted
25 | * key text. This class supports loading both private and public keys.
26 | *
27 | * The utility methods in this class are useful for scenarios where ECDSA keys need to be loaded
28 | * from PEM-formatted strings for cryptographic operations.
29 | *
30 | * @author zihluwang
31 | * @version 2.0.0
32 | * @since 1.6.0
33 | */
34 | public interface KeyLoader {
35 |
36 | /**
37 | * Load private key from pem-formatted key text.
38 | *
39 | * @param pemKeyText pem-formatted key text
40 | * @return loaded private key
41 | */
42 | PrivateKey loadPrivateKey(String pemKeyText);
43 |
44 | /**
45 | * Load public key from pem-formatted key text.
46 | *
47 | * @param pemKeyText pem-formatted key text
48 | * @return loaded private key
49 | */
50 | PublicKey loadPublicKey(String pemKeyText);
51 |
52 | /**
53 | * Retrieves the raw content of a PEM formatted key by removing unnecessary headers, footers,
54 | * and new line characters.
55 | *
56 | *
57 | * This method processes the provided PEM key text to return a cleaned string that contains
58 | * only the key content. The method strips away the
59 | * {@code "-----BEGIN (EC )?(PRIVATE|PUBLIC) KEY-----"} and
60 | * {@code "-----END (EC )?(PRIVATE|PUBLIC) KEY-----"} lines, as well as any new line characters,
61 | * resulting in a continuous string representation of the key, which can be used for further
62 | * cryptographic operations.
63 | *
64 | * @param pemKeyText the PEM formatted key as a string, which may include headers, footers and
65 | * line breaks
66 | * @return a string containing the raw key content devoid of any unnecessary formatting
67 | * or whitespace
68 | */
69 | default String getRawContent(String pemKeyText) {
70 | // remove all unnecessary parts of the pem key text
71 | return pemKeyText
72 | .replaceAll("-----BEGIN ((EC )|(RSA ))?(PRIVATE|PUBLIC) KEY-----", "")
73 | .replaceAll("-----END ((EC )|(RSA ))?(PRIVATE|PUBLIC) KEY-----", "")
74 | .replaceAll("\n", "");
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/key-pair-loader/src/main/java/com/onixbyte/security/exception/KeyLoadingException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.security.exception;
19 |
20 | /**
21 | * The {@code KeyLoadingException} class represents an exception that is thrown when there is an
22 | * error loading cryptographic keys. This exception can be used to indicate various issues such as
23 | * invalid key specifications, unsupported key algorithms, or other key loading errors.
24 | *
25 | * This class extends {@link RuntimeException}, allowing it to be thrown without being declared in
26 | * a method's {@code throws} clause.
27 | *
28 | *
29 | * Example usage:
30 | * {@code
31 | * try {
32 | * KeyLoader keyLoader = new EcKeyLoader();
33 | * ECPrivateKey privateKey = keyLoader.loadPrivateKey(pemPrivateKey);
34 | * } catch (KeyLoadingException e) {
35 | * // Handle the exception
36 | * e.printStackTrace();
37 | * }
38 | * }
39 | *
40 | * @author zihluwang
41 | * @version 2.0.0
42 | * @since 1.6.0
43 | */
44 | public class KeyLoadingException extends RuntimeException {
45 |
46 | /**
47 | * Creates a new instance of {@code KeyLoadingException} without a specific message or cause.
48 | */
49 | public KeyLoadingException() {
50 | }
51 |
52 | /**
53 | * Creates a new instance of {@code KeyLoadingException} with the specified detail message.
54 | *
55 | * @param message the detail message
56 | */
57 | public KeyLoadingException(String message) {
58 | super(message);
59 | }
60 |
61 | /**
62 | * Creates a new instance of {@code KeyLoadingException} with the specified detail message
63 | * and cause.
64 | *
65 | * @param message the detail message
66 | * @param cause the cause of this exception
67 | */
68 | public KeyLoadingException(String message, Throwable cause) {
69 | super(message, cause);
70 | }
71 |
72 | /**
73 | * Creates a new instance of {@code KeyLoadingException} with the specified cause.
74 | *
75 | * @param cause the cause of this exception
76 | */
77 | public KeyLoadingException(Throwable cause) {
78 | super(cause);
79 | }
80 |
81 | /**
82 | * Constructs a new exception with the specified detail message, cause, suppression enabled
83 | * or disabled, and writable stack trace enabled or disabled.
84 | *
85 | * @param message the detail message
86 | * @param cause the cause of this exception
87 | * @param enableSuppression whether suppression is enabled or disabled
88 | * @param writableStackTrace whether the stack trace should be writable
89 | */
90 | public KeyLoadingException(String message,
91 | Throwable cause,
92 | boolean enableSuppression,
93 | boolean writableStackTrace) {
94 | super(message, cause, enableSuppression, writableStackTrace);
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/key-pair-loader/src/main/java/com/onixbyte/security/impl/EcKeyLoader.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.security.impl;
19 |
20 | import com.onixbyte.security.KeyLoader;
21 | import com.onixbyte.security.exception.KeyLoadingException;
22 |
23 | import java.security.KeyFactory;
24 | import java.security.NoSuchAlgorithmException;
25 | import java.security.interfaces.ECPrivateKey;
26 | import java.security.interfaces.ECPublicKey;
27 | import java.security.spec.InvalidKeySpecException;
28 | import java.security.spec.PKCS8EncodedKeySpec;
29 | import java.security.spec.X509EncodedKeySpec;
30 | import java.util.Base64;
31 |
32 | /**
33 | * Key pair loader for loading key pairs for ECDSA-based algorithms.
34 | *
35 | *
36 | * Example usage for ECDSA:
37 | *
{@code
38 | * KeyLoader keyLoader = new EcKeyLoader();
39 | * String pemPrivateKey = """
40 | * -----BEGIN EC PRIVATE KEY-----
41 | * ...
42 | * -----END EC PRIVATE KEY-----""";
43 | * ECPrivateKey privateKey = KeyLoader.loadEcdsaPrivateKey(pemPrivateKey);
44 | *
45 | * String pemPublicKey = """
46 | * -----BEGIN EC PUBLIC KEY-----
47 | * ...
48 | * -----END EC PUBLIC KEY-----""";
49 | * ECPublicKey publicKey = KeyLoader.loadPublicKey(pemPublicKey);
50 | * }
51 | *
52 | * @author zihluwang
53 | * @version 2.0.0
54 | * @since 2.0.0
55 | */
56 | public class EcKeyLoader implements KeyLoader {
57 |
58 | private final KeyFactory keyFactory;
59 |
60 | private final Base64.Decoder decoder;
61 |
62 | /**
63 | * Initialise a key loader for EC-based algorithms.
64 | */
65 | public EcKeyLoader() {
66 | try {
67 | this.keyFactory = KeyFactory.getInstance("EC");
68 | this.decoder = Base64.getDecoder();
69 | } catch (NoSuchAlgorithmException e) {
70 | throw new KeyLoadingException(e);
71 | }
72 | }
73 |
74 | /**
75 | * Load ECDSA private key from pem-formatted key text.
76 | *
77 | * @param pemKeyText pem-formatted key text
78 | * @return loaded private key
79 | * @throws KeyLoadingException if the generated key is not a {@link ECPrivateKey} instance,
80 | * or EC Key Factory is not loaded, or key spec is invalid
81 | */
82 | @Override
83 | public ECPrivateKey loadPrivateKey(String pemKeyText) {
84 | try {
85 | pemKeyText = getRawContent(pemKeyText);
86 | var decodedKeyString = decoder.decode(pemKeyText);
87 | var keySpec = new PKCS8EncodedKeySpec(decodedKeyString);
88 |
89 | var _key = keyFactory.generatePrivate(keySpec);
90 | if (_key instanceof ECPrivateKey privateKey) {
91 | return privateKey;
92 | } else {
93 | throw new KeyLoadingException("Unable to load private key from pem-formatted key text.");
94 | }
95 | } catch (InvalidKeySpecException e) {
96 | throw new KeyLoadingException("Key spec is invalid.", e);
97 | }
98 | }
99 |
100 | /**
101 | * Load public key from pem-formatted key text.
102 | *
103 | * @param pemKeyText pem-formatted key text
104 | * @return loaded private key
105 | * @throws KeyLoadingException if the generated key is not a {@link ECPrivateKey} instance,
106 | * or EC Key Factory is not loaded, or key spec is invalid
107 | */
108 | @Override
109 | public ECPublicKey loadPublicKey(String pemKeyText) {
110 | try {
111 | pemKeyText = getRawContent(pemKeyText);
112 | var keyBytes = decoder.decode(pemKeyText);
113 | var spec = new X509EncodedKeySpec(keyBytes);
114 | var key = keyFactory.generatePublic(spec);
115 | if (key instanceof ECPublicKey publicKey) {
116 | return publicKey;
117 | } else {
118 | throw new KeyLoadingException("Unable to load public key from pem-formatted key text.");
119 | }
120 | } catch (InvalidKeySpecException e) {
121 | throw new KeyLoadingException("Key spec is invalid.", e);
122 | }
123 | }
124 |
125 | }
126 |
--------------------------------------------------------------------------------
/key-pair-loader/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | ${COLOURFUL_OUTPUT}
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/key-pair-loader/src/test/resources/ec_private_key.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN PRIVATE KEY-----
2 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgs79JlARgXEf6EDV7
3 | +PHQCTHEMtqIoHOy1GZ1+ynQJ6yhRANCAARkA7GRY2i4gg8qx0XViAXUP9cPw9pn
4 | Jg1wfrQ41FaMyqVBejNYxvaLtamErF/ySimnjafMJ+VZCh34lBj6Ez8R
5 | -----END PRIVATE KEY-----
6 |
--------------------------------------------------------------------------------
/key-pair-loader/src/test/resources/ec_public_key.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN PUBLIC KEY-----
2 | MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZAOxkWNouIIPKsdF1YgF1D/XD8Pa
3 | ZyYNcH60ONRWjMqlQXozWMb2i7WphKxf8kopp42nzCflWQod+JQY+hM/EQ==
4 | -----END PUBLIC KEY-----
5 |
--------------------------------------------------------------------------------
/key-pair-loader/src/test/resources/rsa_private_key.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN PRIVATE KEY-----
2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQD4VIFYJFMAs15j
3 | J3V3IicHd7sI2TIFqTZME40zlOlVAlPKLZmTQvZFLNgaUAAsvPi5i1DR2ywwK6Al
4 | BfnwVnzvmDXC5mKHOz4oxOQVA6Nlp2yVaQMzidmfYNSkMtcv/4HRPsatc7K/M5l6
5 | pCP20DVRjkikBdIy8e9w+x6BrIFp5Q8PZc/X2BGNAUMMYACdeYH5R/A0CxqkND13
6 | esc4gkynMOrvZrZGHCz51usfSCqyDWWwsN+GG6LYWia4GkNlS0erQnP8gS93dfjl
7 | e96BIfy3z7Iv+kUrf5ikNW2P8jMxLAv6LO+dcUAu9k477wIAF7Iq5KMuH/otsDOu
8 | +h+2qXmBAgMBAAECggEAdRqcmC0g+y6arxV3fkObthjPGYAa57KBCWUa7B0n30+m
9 | pavVRS2Jpttb2SSqwG4ouI6rARti/iBEd9EWqTCP4AieKZetFOpqCJ24lPRPRGus
10 | d9S6jr5N4qut+vSCp37NABijZj4uJ540nTH0R7qtuhTnynl4Q0/1wwiYvTvVF1Lg
11 | dn+I/8aRbshwDhdAOWOUe6GL7/eaCYgN8/UmlKIpp8tg0w2iWxbaFiR7gZiM41LA
12 | M6SXXfcCas+ZVXsGbzQ3SNiVurCGuuRNcCScXS3/WoEDIb3cNtp49iOmQS+nmEoo
13 | wh4uiEd+0+BrzxngS4o5+mKnHJnwgY0+veGVYLMR5QKBgQD9WKQmevMDU5c+NPq9
14 | 8jaR457Fuxq1gwzeFNJdWfOc/K2LEWh+nFNFCb++EboEj6FdxWaWNMxbrmJps5gs
15 | EoBUYy/Tl7UycDqDfiYLmDdTsf2pVjjh9jaIADiLcJ8S6wwJMZKub7Tp8UVkenAl
16 | 535MqShLUC11Y7VxLb3Tsll4XwKBgQD67mm6iCmshr/eszPfNE3ylZ+PiNa7nat7
17 | N7lQzBIiRJflT1kmVidC5gE+jASqH728ChkZZKxbHsjxpmWdAhLOITdXoTB4sDsd
18 | wtV1lxkXxK9FnrpFvO3y1wZ/QsD3Z2KXxHYZqawkUETO9F3nqAXW0b2GDar5Qiyo
19 | J3Tx/43aHwKBgDC0NMJtCoDONhowZy/S+6iqQKC0qprQec3L5PErVMkOTnKYwyTr
20 | +pogGKt6ju9HiXcUdvdTaSIK8UJu00dNuzv94XjlBmGO78DNpJTAC4rcge5m9AKE
21 | qdEVcclkukARzbuKuy8rrHT4/CUn4J141m/4aRWpcUPLCluato6XD9ozAoGBANvf
22 | JhOFFgcPd3YazfvpZ9eE1XA+tfFlYYmxNRcgCU+vjO0oDvSxjutmgHae18N91pG6
23 | w21lskSRf/+GDwl5dKLbphOJsOA/gz07qDDGOf2CoRW+1Hcg6drcINxH0K+4DkLv
24 | qZApBSY4k2JH6zR+HMeztn6M4WBRZLHfCPC3PUN/AoGAA3AoHbLTZvqMIKSDkP4Y
25 | U/tTsSFDY4aYo7LG/jk8af3oPU3KyGh4ZFBd6aMmXbS8f8FjvmrM+/e+y9OOGAlq
26 | iOl0hYrs5cJSMLW6i4KnJYuYbMkgmk3bN2t9apu64xKR94gbPrI6AGnPZp+iIzp0
27 | hXKe4HcuhQ3G0a2hjayiQ84=
28 | -----END PRIVATE KEY-----
29 |
--------------------------------------------------------------------------------
/key-pair-loader/src/test/resources/rsa_public_key.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN PUBLIC KEY-----
2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+FSBWCRTALNeYyd1dyIn
3 | B3e7CNkyBak2TBONM5TpVQJTyi2Zk0L2RSzYGlAALLz4uYtQ0dssMCugJQX58FZ8
4 | 75g1wuZihzs+KMTkFQOjZadslWkDM4nZn2DUpDLXL/+B0T7GrXOyvzOZeqQj9tA1
5 | UY5IpAXSMvHvcPsegayBaeUPD2XP19gRjQFDDGAAnXmB+UfwNAsapDQ9d3rHOIJM
6 | pzDq72a2Rhws+dbrH0gqsg1lsLDfhhui2FomuBpDZUtHq0Jz/IEvd3X45XvegSH8
7 | t8+yL/pFK3+YpDVtj/IzMSwL+izvnXFALvZOO+8CABeyKuSjLh/6LbAzrvoftql5
8 | gQIDAQAB
9 | -----END PUBLIC KEY-----
10 |
--------------------------------------------------------------------------------
/map-util-unsafe/README.md:
--------------------------------------------------------------------------------
1 | # Map Util Unsafe
2 |
3 | `map-util-unsafe` provides a set of more convenient utilities for converting Java bean to Map or Map to Java bean, but which are less safe than the `MapUtil` provided in `devkit-utils`.
4 |
5 | This `MapUtil` is implemented with Reflect API, which might be removed in later JDKs.
--------------------------------------------------------------------------------
/map-util-unsafe/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | import java.net.URI
18 |
19 | plugins {
20 | java
21 | id("java-library")
22 | id("maven-publish")
23 | id("signing")
24 | }
25 |
26 | val artefactVersion: String by project
27 | val projectUrl: String by project
28 | val projectGithubUrl: String by project
29 | val licenseName: String by project
30 | val licenseUrl: String by project
31 |
32 | group = "com.onixbyte"
33 | version = artefactVersion
34 |
35 | repositories {
36 | mavenCentral()
37 | }
38 |
39 | java {
40 | sourceCompatibility = JavaVersion.VERSION_17
41 | targetCompatibility = JavaVersion.VERSION_17
42 | withSourcesJar()
43 | withJavadocJar()
44 | }
45 |
46 | tasks.withType {
47 | options.encoding = "UTF-8"
48 | }
49 |
50 | tasks.withType {
51 | exclude("logback.xml")
52 | }
53 |
54 | dependencies {
55 | compileOnly(libs.slf4j)
56 | implementation(libs.logback)
57 | api(project(":devkit-core"))
58 | testImplementation(platform(libs.junit.bom))
59 | testImplementation(libs.junit.jupiter)
60 | }
61 |
62 | tasks.test {
63 | useJUnitPlatform()
64 | }
65 |
66 | publishing {
67 | publications {
68 | create("mapUtilUnsafe") {
69 | groupId = group.toString()
70 | artifactId = "map-util-unsafe"
71 | version = artefactVersion
72 |
73 | pom {
74 | name = "Unsafe Map Util"
75 | description =
76 | "This module is a converter that can convert an object to a map with unsafe methods."
77 | url = projectUrl
78 |
79 | licenses {
80 | license {
81 | name = licenseName
82 | url = licenseUrl
83 | }
84 | }
85 |
86 | scm {
87 | connection = "scm:git:git://github.com:OnixByte/JDevKit.git"
88 | developerConnection = "scm:git:git://github.com:OnixByte/JDevKit.git"
89 | url = projectGithubUrl
90 | }
91 |
92 | developers {
93 | developer {
94 | id = "zihluwang"
95 | name = "Zihlu Wang"
96 | email = "really@zihlu.wang"
97 | timezone = "Asia/Hong_Kong"
98 | }
99 |
100 | developer {
101 | id = "siujamo"
102 | name = "Siu Jam'o"
103 | email = "jamo.siu@outlook.com"
104 | timezone = "Asia/Shanghai"
105 | }
106 | }
107 | }
108 |
109 | from(components["java"])
110 |
111 | signing {
112 | sign(publishing.publications["mapUtilUnsafe"])
113 | }
114 | }
115 |
116 | repositories {
117 | maven {
118 | name = "sonatypeNexus"
119 | url = URI(providers.gradleProperty("repo.maven-central.host").get())
120 | credentials {
121 | username = providers.gradleProperty("repo.maven-central.username").get()
122 | password = providers.gradleProperty("repo.maven-central.password").get()
123 | }
124 | }
125 | }
126 | }
127 | }
--------------------------------------------------------------------------------
/map-util-unsafe/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
22 |
24 |
25 |
26 |
27 |
28 | ${COLOURFUL_OUTPUT}
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/num4j/README.md:
--------------------------------------------------------------------------------
1 | # Num4j
2 |
3 | `num4j` provides some mathematical algorithms and utilities such as chained high-precision mathematical calculator and percentile statistic algorithm.
--------------------------------------------------------------------------------
/num4j/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | import java.net.URI
18 |
19 | plugins {
20 | java
21 | id("java-library")
22 | id("maven-publish")
23 | id("signing")
24 | }
25 |
26 | val artefactVersion: String by project
27 | val projectUrl: String by project
28 | val projectGithubUrl: String by project
29 | val licenseName: String by project
30 | val licenseUrl: String by project
31 |
32 | group = "com.onixbyte"
33 | version = artefactVersion
34 |
35 | repositories {
36 | mavenCentral()
37 | }
38 |
39 | java {
40 | sourceCompatibility = JavaVersion.VERSION_17
41 | targetCompatibility = JavaVersion.VERSION_17
42 | withSourcesJar()
43 | withJavadocJar()
44 | }
45 |
46 | tasks.withType {
47 | options.encoding = "UTF-8"
48 | }
49 |
50 | tasks.withType {
51 | exclude("logback.xml")
52 | }
53 |
54 | dependencies {
55 | compileOnly(libs.slf4j)
56 | implementation(libs.logback)
57 | api(project(":devkit-core"))
58 | testImplementation(platform(libs.junit.bom))
59 | testImplementation(libs.junit.jupiter)
60 | }
61 |
62 | tasks.test {
63 | useJUnitPlatform()
64 | }
65 |
66 | publishing {
67 | publications {
68 | create("num4j") {
69 | groupId = group.toString()
70 | artifactId = "num4j"
71 | version = artefactVersion
72 |
73 | pom {
74 | name = "Num4j"
75 | description =
76 | "This module is an easy-to-use util for mathematical calculations in Java."
77 | url = projectUrl
78 |
79 | licenses {
80 | license {
81 | name = licenseName
82 | url = licenseUrl
83 | }
84 | }
85 |
86 | scm {
87 | connection = "scm:git:git://github.com:OnixByte/JDevKit.git"
88 | developerConnection = "scm:git:git://github.com:OnixByte/JDevKit.git"
89 | url = projectGithubUrl
90 | }
91 |
92 | developers {
93 | developer {
94 | id = "zihluwang"
95 | name = "Zihlu Wang"
96 | email = "really@zihlu.wang"
97 | timezone = "Asia/Hong_Kong"
98 | }
99 |
100 | developer {
101 | id = "siujamo"
102 | name = "Siu Jam'o"
103 | email = "jamo.siu@outlook.com"
104 | timezone = "Asia/Shanghai"
105 | }
106 | }
107 | }
108 |
109 | from(components["java"])
110 |
111 | signing {
112 | sign(publishing.publications["num4j"])
113 | }
114 | }
115 |
116 | repositories {
117 | maven {
118 | name = "sonatypeNexus"
119 | url = URI(providers.gradleProperty("repo.maven-central.host").get())
120 | credentials {
121 | username = providers.gradleProperty("repo.maven-central.username").get()
122 | password = providers.gradleProperty("repo.maven-central.password").get()
123 | }
124 | }
125 | }
126 | }
127 | }
--------------------------------------------------------------------------------
/num4j/src/main/java/com/onixbyte/nums/PercentileCalculator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.nums;
19 |
20 | import com.onixbyte.nums.model.QuartileBounds;
21 | import org.slf4j.Logger;
22 | import org.slf4j.LoggerFactory;
23 |
24 | import java.util.List;
25 |
26 | /**
27 | * A utility class that provides methods for calculating percentiles and interquartile range (IQR)
28 | * bounds for a dataset.
29 | *
30 | * This class contains static methods to:
31 | *
32 | * - Calculate a specified percentile from a list of double values using linear interpolation.
33 | * - Calculate interquartile bounds (Q1, Q3) and the corresponding lower and upper bounds,
34 | * which can be used to identify outliers in the dataset.
35 | *
36 | *
37 | * This class is final, meaning it cannot be subclassed, and it only contains static methods,
38 | * so instances of the class cannot be created.
39 | *
Example usage:
40 | *
41 | * {@code
42 | * List data = Arrays.asList(1.0, 2.0, 3.0, 4.0, 5.0);
43 | * Double percentileValue = PercentileCalculator.calculatePercentile(data, 50.0); // Calculates median
44 | * QuartileBounds bounds = PercentileCalculator.calculatePercentileBounds(data); // Calculates IQR bounds
45 | * }
46 | *
47 | *
48 | * @author zihluwang
49 | * @version 1.6.5
50 | * @since 1.6.5
51 | */
52 | public final class PercentileCalculator {
53 |
54 | private final static Logger log = LoggerFactory.getLogger(PercentileCalculator.class);
55 |
56 | /**
57 | * Private constructor to prevent instantiation of this utility class.
58 | */
59 | private PercentileCalculator() {
60 | }
61 |
62 | /**
63 | * Calculates the specified percentile from a list of values.
64 | *
65 | * This method takes a list of double values and calculates the given percentile using linear
66 | * interpolation between the two closest ranks. The list is first sorted in ascending order,
67 | * and the specified percentile is then calculated.
68 | *
69 | * @param values a list of {@code Double} values from which the percentile is calculated.
70 | * @param percentile a {@code Double} representing the percentile to be calculated (e.g., 50.0
71 | * for the median)
72 | * @return a {@code Double} value representing the calculated percentile
73 | */
74 | public static Double calculatePercentile(List values, Double percentile) {
75 | if (values.isEmpty()) {
76 | throw new IllegalArgumentException("Unable to sort an empty list.");
77 | }
78 | var sorted = values.stream().sorted().toList();
79 |
80 | var rank = percentile / 100. * (sorted.size() - 1);
81 | var lowerIndex = (int) Math.floor(rank);
82 | var upperIndex = (int) Math.ceil(rank);
83 | var weight = rank - lowerIndex;
84 |
85 | return sorted.get(lowerIndex) * (1 - weight) + sorted.get(upperIndex) * weight;
86 | }
87 |
88 | /**
89 | * Calculates the interquartile range (IQR) and the corresponding lower and upper bounds
90 | * based on the first (Q1) and third (Q3) quartiles of a dataset.
91 | *
92 | * This method takes a list of double values, calculates the first quartile (Q1),
93 | * the third quartile (Q3), and the interquartile range (IQR). Using the IQR, it computes
94 | * the lower and upper bounds, which can be used to detect outliers in the dataset.
95 | * The lower bound is defined as {@code Q1 - 1.5 * IQR}, and the upper bound is defined as
96 | * {@code Q3 + 1.5 * IQR}.
97 | *
98 | * @param data a list of {@code Double} values for which the quartile bounds will be calculated
99 | * @return a {@code QuartileBounds} object containing the calculated lower and upper bounds
100 | */
101 | public static QuartileBounds calculatePercentileBounds(List data) {
102 | var sorted = data.stream().sorted().toList();
103 | var Q1 = calculatePercentile(sorted, 25.);
104 | var Q3 = calculatePercentile(sorted, 75.);
105 |
106 | var IQR = Q3 - Q1;
107 |
108 | var lowerBound = Q1 - 1.5 * IQR;
109 | var upperBound = Q3 + 1.5 * IQR;
110 |
111 | return QuartileBounds.builder()
112 | .upperBound(upperBound)
113 | .lowerBound(lowerBound)
114 | .build();
115 | }
116 |
117 | }
118 |
--------------------------------------------------------------------------------
/num4j/src/main/java/com/onixbyte/nums/model/QuartileBounds.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.nums.model;
19 |
20 | /**
21 | * A record representing the quartile bounds of a dataset.
22 | *
23 | * This class encapsulates the lower and upper bounds of a dataset, which are typically used for
24 | * detecting outliers in the data. The bounds are calculated based on the interquartile range (IQR)
25 | * of the dataset. Values below the lower bound or above the upper bound may be considered outliers.
26 | *
27 | * Quartile bounds consist of:
28 | *
29 | * - {@code lowerBound} - The lower bound of the dataset, typically {@code Q1 - 1.5 * IQR}.
30 | * - {@code upperBound} - The upper bound of the dataset, typically {@code Q3 + 1.5 * IQR}.
31 | *
32 | *
33 | * Example usage:
34 | *
35 | * QuartileBounds bounds = QuartileBounds.builder()
36 | * .lowerBound(1.5)
37 | * .upperBound(7.5)
38 | * .build();
39 | *
40 | *
41 | * @param upperBound the upper bound of the dataset
42 | * @param lowerBound the lower bound of the dataset
43 | * @author zihluwang
44 | * @version 1.6.5
45 | * @since 1.6.5
46 | */
47 | public record QuartileBounds(
48 | Double upperBound,
49 | Double lowerBound
50 | ) {
51 |
52 | /**
53 | * Creates a new {@link Builder} instance for building a {@code QuartileBounds} object.
54 | *
55 | * The {@link Builder} pattern is used to construct the {@code QuartileBounds} object with
56 | * optional values for the upper and lower bounds.
57 | *
58 | *
59 | * @return a new instance of the {@link Builder} class
60 | */
61 | public static Builder builder() {
62 | return new Builder();
63 | }
64 |
65 | /**
66 | * A builder class for constructing instances of the {@code QuartileBounds} record.
67 | *
68 | * The {@link Builder} pattern allows for the step-by-step construction of a
69 | * {@code QuartileBounds} object, providing a flexible way to set values for the lower and
70 | * upper bounds. Once the builder has the required values, the {@link #build()} method creates
71 | * and returns a new {@code QuartileBounds} object.
72 | *
73 | *
74 | * Example usage:
75 | *
76 | * {@code
77 | * QuartileBounds bounds = QuartileBounds.builder()
78 | * .lowerBound(1.5)
79 | * .upperBound(7.5)
80 | * .build();
81 | * }
82 | *
83 | */
84 | public static class Builder {
85 | private Double upperBound;
86 | private Double lowerBound;
87 |
88 | /**
89 | * Private constructor to prevent instantiation of this utility class.
90 | */
91 | private Builder() {
92 | }
93 |
94 | /**
95 | * Sets the upper bound for the {@code QuartileBounds}.
96 | *
97 | * @param upperBound the upper bound of the dataset
98 | * @return the current {@code Builder} instance, for method chaining
99 | */
100 | public Builder upperBound(Double upperBound) {
101 | this.upperBound = upperBound;
102 | return this;
103 | }
104 |
105 | /**
106 | * Sets the lower bound for the {@code QuartileBounds}.
107 | *
108 | * @param lowerBound the lower bound of the dataset
109 | * @return the current {@code Builder} instance, for method chaining
110 | */
111 | public Builder lowerBound(Double lowerBound) {
112 | this.lowerBound = lowerBound;
113 | return this;
114 | }
115 |
116 | /**
117 | * Builds and returns a new {@code QuartileBounds} instance with the specified upper and
118 | * lower bounds.
119 | *
120 | * @return a new {@code QuartileBounds} object containing the specified bounds
121 | */
122 | public QuartileBounds build() {
123 | return new QuartileBounds(upperBound, lowerBound);
124 | }
125 | }
126 |
127 | }
128 |
--------------------------------------------------------------------------------
/num4j/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | ${COLOURFUL_OUTPUT}
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/property-guard-spring-boot-starter/README.md:
--------------------------------------------------------------------------------
1 | # Property Guard
2 |
3 | `property-guard-spring-boot-starter` is a utility that can help you protect secret values in Spring Boot configurations.
4 |
5 | ## Example usage
6 |
7 | ### 1. Implementation this module
8 |
9 | ```kotlin
10 | dependencies {
11 | implementation(platform("com.onixbyte:devkit-bom:$devKitVersion"))
12 | implementation("com.onixbyte:devkit-utils")
13 | implementation("com.onixbyte:property-guard-spring-boot-starter")
14 | }
15 | ```
16 |
17 | ### 2. Generate a secret
18 |
19 | Use the following codes to get a random secret.
20 |
21 | ```java
22 | @SpringBootTest
23 | class SpringBootApplicationTest {
24 |
25 | @Test
26 | void contextLoads() {
27 | System.out.println(AesUtil.generateRandomSecret()); // Output: a 16-char long secret
28 | }
29 | }
30 | ```
31 |
32 | Or you can write a 16-char long secret by yourself.
33 |
34 | ### 3. Encrypt your secret properties and place them into your configuration file
35 |
36 | ### 4. Run application with parameter `--pg.key=$your_secret`
37 |
38 |
--------------------------------------------------------------------------------
/property-guard-spring-boot-starter/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | import java.net.URI
18 |
19 | plugins {
20 | java
21 | id("java-library")
22 | id("maven-publish")
23 | id("signing")
24 | }
25 |
26 | val artefactVersion: String by project
27 | val projectUrl: String by project
28 | val projectGithubUrl: String by project
29 | val licenseName: String by project
30 | val licenseUrl: String by project
31 |
32 | group = "com.onixbyte"
33 | version = artefactVersion
34 |
35 | repositories {
36 | mavenCentral()
37 | }
38 |
39 | java {
40 | sourceCompatibility = JavaVersion.VERSION_17
41 | targetCompatibility = JavaVersion.VERSION_17
42 | withSourcesJar()
43 | withJavadocJar()
44 | }
45 |
46 | tasks.withType {
47 | options.encoding = "UTF-8"
48 | }
49 |
50 | tasks.withType {
51 | exclude("logback.xml")
52 | }
53 |
54 | dependencies {
55 | compileOnly(libs.slf4j)
56 | implementation(libs.logback)
57 | api(project(":devkit-core"))
58 | api(project(":devkit-utils"))
59 | implementation(libs.springBoot.autoconfigure)
60 | implementation(libs.springBoot.starter.logging)
61 | implementation(libs.springBoot.configurationProcessor)
62 |
63 | testImplementation(platform(libs.junit.bom))
64 | testImplementation(libs.junit.jupiter)
65 | testImplementation(libs.springBoot.starter.test)
66 | }
67 |
68 | tasks.test {
69 | useJUnitPlatform()
70 | }
71 |
72 | publishing {
73 | publications {
74 | create("propertyGuardSpringBootStarter") {
75 | groupId = group.toString()
76 | artifactId = "property-guard-spring-boot-starter"
77 | version = artefactVersion
78 |
79 | pom {
80 | name = "Property Guard Spring Boot Starter"
81 | description = "This module is made for protecting your secret properties in your spring application properties."
82 | url = projectUrl
83 |
84 | licenses {
85 | license {
86 | name = licenseName
87 | url = licenseUrl
88 | }
89 | }
90 |
91 | scm {
92 | connection = "scm:git:git://github.com:OnixByte/JDevKit.git"
93 | developerConnection = "scm:git:git://github.com:OnixByte/JDevKit.git"
94 | url = projectGithubUrl
95 | }
96 |
97 | developers {
98 | developer {
99 | id = "zihluwang"
100 | name = "Zihlu Wang"
101 | email = "really@zihlu.wang"
102 | timezone = "Asia/Hong_Kong"
103 | }
104 |
105 | developer {
106 | id = "siujamo"
107 | name = "Siu Jam'o"
108 | email = "jamo.siu@outlook.com"
109 | timezone = "Asia/Shanghai"
110 | }
111 | }
112 | }
113 |
114 | from(components["java"])
115 |
116 | signing {
117 | sign(publishing.publications["propertyGuardSpringBootStarter"])
118 | }
119 | }
120 |
121 | repositories {
122 | maven {
123 | name = "sonatypeNexus"
124 | url = URI(providers.gradleProperty("repo.maven-central.host").get())
125 | credentials {
126 | username = providers.gradleProperty("repo.maven-central.username").get()
127 | password = providers.gradleProperty("repo.maven-central.password").get()
128 | }
129 | }
130 | }
131 | }
132 | }
--------------------------------------------------------------------------------
/property-guard-spring-boot-starter/src/main/resources/META-INF/spring.factories:
--------------------------------------------------------------------------------
1 | org.springframework.boot.env.EnvironmentPostProcessor=com.onixbyte.propertyguard.autoconfiguration.PropertyGuard
--------------------------------------------------------------------------------
/property-guard-spring-boot-starter/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | ${COLOURFUL_OUTPUT}
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | rootProject.name = "JDevKit"
19 |
20 | include(
21 | "devkit-bom",
22 | "devkit-core",
23 | "devkit-utils",
24 | "guid",
25 | "key-pair-loader",
26 | "map-util-unsafe",
27 | "num4j",
28 | "simple-jwt",
29 | "simple-jwt-facade",
30 | "simple-jwt-authzero",
31 | "simple-jwt-spring-boot-starter",
32 | "property-guard-spring-boot-starter",
33 | "simple-serial-spring-boot-starter"
34 | )
35 |
--------------------------------------------------------------------------------
/simple-jwt-authzero/README.md:
--------------------------------------------------------------------------------
1 | # Module `simple-jwt-authzero`
2 |
3 | ## Introduction
4 |
5 | The `simple-jwt-authzero` module is an implementation of module `simple-jwt-facade` which uses the third-party library `com.auth0:java-jwt`. By using this module can help you use JWT to help you in your application.
6 |
7 | ## Prerequisites
8 |
9 | This whole `JDevKit` is developed by **JDK 17**, which means you have to use JDK 17 for better experience.
10 |
11 | ## Installation
12 |
13 | > This module has already imported `simple-jwt-facade`, there is no need to import it again.
14 |
15 | ### If you are using `Maven`
16 |
17 | It is quite simple to install this module by `Maven`. The only thing you need to do is find your `pom.xml` file in the project, then find the `` node in the `` node, and add the following codes to `` node:
18 |
19 | ```xml
20 |
21 | cn.org.codecrafters
22 | simple-jwt-authzero
23 | ${simple-jwt-authzero.version}
24 |
25 | ```
26 |
27 | And run `mvn dependency:get` in your project root folder(i.e., if your `pom.xml` is located at `/path/to/your/project/pom.xml`, then your current work folder should be `/path/to/your/project`), then `Maven` will automatically download the `jar` archive from `Maven Central Repository`. This could be **MUCH EASIER** if you are using IDE(i.e., IntelliJ IDEA), the only thing you need to do is click the refresh button of `Maven`.
28 |
29 | If you are restricted using the Internet, and have to make `Maven` offline, you could follow the following steps.
30 |
31 | 1. Download the `jar` file from any place you can get and transfer the `jar` files to your work computer.
32 | 2. Move the `jar` files to your local `Maven` Repository as the path of `/path/to/maven_local_repo/cn/org/codecrafters/simple-jwt-facade/`.
33 |
34 | ### If you are using `Gradle`
35 |
36 | Add this module to your project with `Gradle` is much easier than doing so with `Maven`.
37 |
38 | Find `build.gradle` in the needed project, and add the following code to the `dependencies` closure in the build script:
39 |
40 | ```groovy
41 | implementation 'cn.org.codecrafters:simple-jwt-authzero:${simple-jwt-authzero.version}'
42 | ```
43 |
44 | ### If you are not using `Maven` or `Gradle`
45 |
46 | 1. Download the `jar` file from the Internet.
47 | 2. Create a folder in your project and name it as a name you like(i.e., for me, I prefer `vendor`).
48 | 3. Put the `jar` file to the folder you just created in Step 2.
49 | 4. Add this folder to your project `classpath`.
50 |
51 | ## Use the `AuthzeroTokenResolver`
52 |
53 | We have implemented `TokenResolver` to make sure you can add JWT to your Java application as soon as possible. All you need to do is to create an instance of `com.onixbyte.simplejwt.authzero.AuthzeroTokenResolver` and other operations to JWT could follow our instruction in [`simple-jwt-facade`](../simple-jwt-facade/README.md).
54 |
55 | ## Contact
56 |
57 | If you have any suggestions, ideas, don't hesitate contacting us via [GitHub Issues](https://github.com/CodeCraftersCN/jdevkit/issues/new) or [Discord Community](https://discord.gg/NQK9tjcBB8).
58 |
59 | If you face any bugs while using our library and you are able to fix any bugs in our library, we would be happy to accept pull requests from you on [GitHub](https://github.com/CodeCraftersCN/jdevkit/compare).
60 |
--------------------------------------------------------------------------------
/simple-jwt-authzero/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | import java.net.URI
18 |
19 | plugins {
20 | java
21 | id("java-library")
22 | id("maven-publish")
23 | id("signing")
24 | }
25 |
26 | val artefactVersion: String by project
27 | val projectUrl: String by project
28 | val projectGithubUrl: String by project
29 | val licenseName: String by project
30 | val licenseUrl: String by project
31 |
32 | group = "com.onixbyte"
33 | version = artefactVersion
34 |
35 | repositories {
36 | mavenCentral()
37 | }
38 |
39 | java {
40 | sourceCompatibility = JavaVersion.VERSION_17
41 | targetCompatibility = JavaVersion.VERSION_17
42 | withSourcesJar()
43 | withJavadocJar()
44 | }
45 |
46 | tasks.withType {
47 | options.encoding = "UTF-8"
48 | }
49 |
50 | tasks.withType {
51 | exclude("logback.xml")
52 | }
53 |
54 | dependencies {
55 | compileOnly(libs.slf4j)
56 | implementation(libs.logback)
57 |
58 | api(project(":devkit-utils"))
59 | api(project(":guid"))
60 | api(project(":key-pair-loader"))
61 | api(project(":simple-jwt-facade"))
62 | api(libs.jackson.databind)
63 | api(libs.jwt.core)
64 |
65 | testImplementation(platform(libs.junit.bom))
66 | testImplementation(libs.junit.jupiter)
67 | }
68 |
69 | tasks.test {
70 | useJUnitPlatform()
71 | }
72 |
73 | publishing {
74 | publications {
75 | create("simpleJwtAuthzero") {
76 | groupId = group.toString()
77 | artifactId = "simple-jwt-authzero"
78 | version = artefactVersion
79 |
80 | pom {
81 | name = "Simple JWT :: Auth0"
82 | description = "Simple JWT implemented with com.auth0:java-jwt."
83 | url = projectUrl
84 |
85 | licenses {
86 | license {
87 | name = licenseName
88 | url = licenseUrl
89 | }
90 | }
91 |
92 | scm {
93 | connection = "scm:git:git://github.com:OnixByte/JDevKit.git"
94 | developerConnection = "scm:git:git://github.com:OnixByte/JDevKit.git"
95 | url = projectGithubUrl
96 | }
97 |
98 | developers {
99 | developer {
100 | id = "zihluwang"
101 | name = "Zihlu Wang"
102 | email = "really@zihlu.wang"
103 | timezone = "Asia/Hong_Kong"
104 | }
105 |
106 | developer {
107 | id = "siujamo"
108 | name = "Siu Jam'o"
109 | email = "jamo.siu@outlook.com"
110 | timezone = "Asia/Shanghai"
111 | }
112 | }
113 | }
114 |
115 | from(components["java"])
116 |
117 | signing {
118 | sign(publishing.publications["simpleJwtAuthzero"])
119 | }
120 | }
121 |
122 | repositories {
123 | maven {
124 | name = "sonatypeNexus"
125 | url = URI(providers.gradleProperty("repo.maven-central.host").get())
126 | credentials {
127 | username = providers.gradleProperty("repo.maven-central.username").get()
128 | password = providers.gradleProperty("repo.maven-central.password").get()
129 | }
130 | }
131 | }
132 | }
133 | }
--------------------------------------------------------------------------------
/simple-jwt-authzero/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | ${COLOURFUL_OUTPUT}
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/simple-jwt-authzero/src/test/java/com/onixbyte/simplejwt/authzero/test/TestAuthzeroTokenResolver.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2023 CodeCraftersCN.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.simplejwt.authzero.test;
19 |
20 | import com.onixbyte.simplejwt.authzero.AuthzeroTokenResolver;
21 | import com.onixbyte.simplejwt.constants.TokenAlgorithm;
22 | import org.junit.jupiter.api.Test;
23 |
24 | import java.time.Duration;
25 |
26 | /**
27 | * TestAuthzeroTokenResolver
28 | *
29 | * @author Zihlu Wang
30 | */
31 | public class TestAuthzeroTokenResolver {
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/simple-jwt-facade/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | import java.net.URI
18 |
19 | plugins {
20 | java
21 | id("java-library")
22 | id("maven-publish")
23 | id("signing")
24 | }
25 |
26 | val artefactVersion: String by project
27 | val projectUrl: String by project
28 | val projectGithubUrl: String by project
29 | val licenseName: String by project
30 | val licenseUrl: String by project
31 |
32 | group = "com.onixbyte"
33 | version = artefactVersion
34 |
35 | repositories {
36 | mavenCentral()
37 | }
38 |
39 | java {
40 | sourceCompatibility = JavaVersion.VERSION_17
41 | targetCompatibility = JavaVersion.VERSION_17
42 | withSourcesJar()
43 | withJavadocJar()
44 | }
45 |
46 | tasks.withType {
47 | options.encoding = "UTF-8"
48 | }
49 |
50 | tasks.withType {
51 | exclude("logback.xml")
52 | }
53 |
54 | dependencies {
55 | compileOnly(libs.slf4j)
56 | implementation(libs.logback)
57 |
58 | api(project(":devkit-core"))
59 | api(project(":devkit-utils"))
60 | api(project(":guid"))
61 |
62 | testImplementation(platform(libs.junit.bom))
63 | testImplementation(libs.junit.jupiter)
64 | }
65 |
66 | tasks.test {
67 | useJUnitPlatform()
68 | }
69 |
70 | publishing {
71 | publications {
72 | create("simpleJwtFacade") {
73 | groupId = group.toString()
74 | artifactId = "simple-jwt-facade"
75 | version = artefactVersion
76 |
77 | pom {
78 | name = "Simple JWT :: Facade"
79 | description = "Declaration of simple JWT module."
80 | url = projectUrl
81 |
82 | licenses {
83 | license {
84 | name = licenseName
85 | url = licenseUrl
86 | }
87 | }
88 |
89 | scm {
90 | connection = "scm:git:git://github.com:OnixByte/JDevKit.git"
91 | developerConnection = "scm:git:git://github.com:OnixByte/JDevKit.git"
92 | url = projectGithubUrl
93 | }
94 |
95 | developers {
96 | developer {
97 | id = "zihluwang"
98 | name = "Zihlu Wang"
99 | email = "really@zihlu.wang"
100 | timezone = "Asia/Hong_Kong"
101 | }
102 |
103 | developer {
104 | id = "siujamo"
105 | name = "Siu Jam'o"
106 | email = "jamo.siu@outlook.com"
107 | timezone = "Asia/Shanghai"
108 | }
109 | }
110 | }
111 |
112 | from(components["java"])
113 |
114 | signing {
115 | sign(publishing.publications["simpleJwtFacade"])
116 | }
117 | }
118 |
119 | repositories {
120 | maven {
121 | name = "sonatypeNexus"
122 | url = URI(providers.gradleProperty("repo.maven-central.host").get())
123 | credentials {
124 | username = providers.gradleProperty("repo.maven-central.username").get()
125 | password = providers.gradleProperty("repo.maven-central.password").get()
126 | }
127 | }
128 | }
129 | }
130 | }
--------------------------------------------------------------------------------
/simple-jwt-facade/src/main/java/com/onixbyte/simplejwt/TokenPayload.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.simplejwt;
19 |
20 | /**
21 | * {@code TokenPayload} interface is used to mark a data class as suitable
22 | * for being used as the payload in a JSON Web Token (JWT). Any class
23 | * implementing this interface can be used to represent the payload data that
24 | * will be included in a JWT.
25 | *
26 | * Implementing this interface indicates that the data class contains
27 | * information that needs to be securely transmitted and verified as part of a
28 | * JWT. The payload typically contains claims or attributes that provide
29 | * additional information about the JWT subject or context.
30 | *
31 | * Usage:
32 | * To use a class as a JWT payload, simply implement the {@code TokenPayload}
33 | * interface in the data class:
34 | *
35 | * public class UserData implements TokenPayload {
36 | * // Class implementation with payload data...
37 | * }
38 | *
39 | *
40 | * @author Zihlu Wang
41 | * @version 1.1.0
42 | * @since 1.0.0
43 | */
44 | public interface TokenPayload {
45 | }
46 |
--------------------------------------------------------------------------------
/simple-jwt-facade/src/main/java/com/onixbyte/simplejwt/annotations/ExcludeFromPayload.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.simplejwt.annotations;
19 |
20 | import java.lang.annotation.ElementType;
21 | import java.lang.annotation.Retention;
22 | import java.lang.annotation.RetentionPolicy;
23 | import java.lang.annotation.Target;
24 |
25 | /**
26 | * Annotation {@code ExcludeFromPayload} is used to mark a property of a data
27 | * class that should be excluded from being automatically injected into the
28 | * JSON Web Token (JWT) payload during token generation. When a property is
29 | * annotated by this annotation, it will not be included in the JWT payloads.
30 | *
31 | * Usage:
32 | * To exclude a property from the JWT payload, annotate the property with
33 | * {@code @ExcludeFromPayload}:
34 | *
35 | *
{@code
36 | * public class UserData implements TokenPayload {
37 | * private String username;
38 | *
39 | * // This property will not be included in the JWT payload
40 | * @ExcludeFromPayload
41 | * private String sensitiveData;
42 | *
43 | * // Getters and setters...
44 | * }
45 | * }
46 | *
47 | * Note:
48 | * This annotation should be used on properties that are not intended to
49 | * be included in the JWT payload due to their sensitive nature or for other
50 | * reasons only. It is important to carefully choose which properties are
51 | * excluded from the payload to ensure the JWT remains secure and efficient.
52 | *
53 | * @author Zihlu Wang
54 | * @version 1.1.0
55 | * @since 1.0.0
56 | */
57 | @Retention(RetentionPolicy.RUNTIME)
58 | @Target({ElementType.FIELD})
59 | public @interface ExcludeFromPayload {
60 | }
61 |
--------------------------------------------------------------------------------
/simple-jwt-facade/src/main/java/com/onixbyte/simplejwt/annotations/TokenEnum.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.simplejwt.annotations;
19 |
20 | import com.onixbyte.simplejwt.constants.TokenDataType;
21 |
22 | import java.lang.annotation.ElementType;
23 | import java.lang.annotation.Retention;
24 | import java.lang.annotation.RetentionPolicy;
25 | import java.lang.annotation.Target;
26 |
27 | /**
28 | * This annotation marks the enum field declared in payload class will be handled as basic data
29 | * types in {@link TokenDataType}.
30 | *
31 | * @author Zihlu Wang
32 | */
33 | @Retention(RetentionPolicy.RUNTIME)
34 | @Target({ElementType.FIELD})
35 | public @interface TokenEnum {
36 |
37 | /**
38 | * The name of the field of the base data corresponding to the enumeration data.
39 | *
40 | * @return the name of the property
41 | */
42 | String propertyName();
43 |
44 | /**
45 | * The attribute {@code dataType} specifies what base data type to treat this enum as.
46 | *
47 | * @return the data type of the token
48 | */
49 | TokenDataType dataType();
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/simple-jwt-facade/src/main/java/com/onixbyte/simplejwt/constants/PredefinedKeys.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.simplejwt.constants;
19 |
20 | import java.util.List;
21 |
22 | /**
23 | * The {@code PredefinedKeys} class contains constants for standard JSON Web Token (JWT) claims. These constants
24 | * represent the names of the standard claims that can be included in a JWT payload. Developers can use these constants
25 | * when working with JWTs to ensure consistent naming of the claims.
26 | *
27 | * The class provides the following standard JWT claim constants:
28 | *
29 | * - {@link #ISSUER}: Represents the "iss" (Issuer) claim.
30 | * - {@link #SUBJECT}: Represents the "sub" (Subject) claim.
31 | * - {@link #AUDIENCE}: Represents the "aud" (Audience) claim.
32 | * - {@link #EXPIRATION_TIME}: Represents the "exp" (Expiration Time) claim.
33 | * - {@link #NOT_BEFORE}: Represents the "nbf" (Not Before) claim.
34 | * - {@link #ISSUED_AT}: Represents the "iat" (Issued At) claim.
35 | * - {@link #JWT_ID}: Represents the "jti" (JWT ID) claim.
36 | *
37 | *
38 | * The class also contains a list of all the standard claim constants, accessible via the {@link
39 | * #KEYS} field. This list can be useful for iterating through all the standard claims or checking
40 | * for the presence of specific claims.
41 | *
42 | * Note: This class is final and cannot be instantiated. It only serves as a utility class to hold
43 | * the standard JWT claim constants.
44 | *
45 | * @author zihluwang
46 | * @version 1.1.0
47 | * @since 1.0.0
48 | */
49 | public final class PredefinedKeys {
50 |
51 | /**
52 | * Constant representing the "iss" (Issuer) claim in a JWT payload.
53 | */
54 | public static final String ISSUER = "iss";
55 |
56 | /**
57 | * Constant representing the "sub" (Subject) claim in a JWT payload.
58 | */
59 | public static final String SUBJECT = "sub";
60 |
61 | /**
62 | * Constant representing the "aud" (Audience) claim in a JWT payload.
63 | */
64 | public static final String AUDIENCE = "aud";
65 |
66 | /**
67 | * Constant representing the "exp" (Expiration Time) claim in a JWT payload.
68 | */
69 | public static final String EXPIRATION_TIME = "exp";
70 |
71 | /**
72 | * Constant representing the "nbf" (Not Before) claim in a JWT payload.
73 | */
74 | public static final String NOT_BEFORE = "nbf";
75 |
76 | /**
77 | * Constant representing the "iat" (Issued At) claim in a JWT payload.
78 | */
79 | public static final String ISSUED_AT = "iat";
80 |
81 | /**
82 | * Constant representing the "jti" (JWT ID) claim in a JWT payload.
83 | */
84 | public static final String JWT_ID = "jti";
85 |
86 | /**
87 | * List containing all the standard JWT claim constants.
88 | */
89 | public static final List KEYS =
90 | List.of(ISSUER, SUBJECT, AUDIENCE, EXPIRATION_TIME, NOT_BEFORE, ISSUED_AT, JWT_ID);
91 |
92 | /**
93 | * Private constructor to prevent instantiation of this utility class.
94 | */
95 | private PredefinedKeys() {
96 | }
97 | }
98 |
99 |
--------------------------------------------------------------------------------
/simple-jwt-facade/src/main/java/com/onixbyte/simplejwt/constants/TokenAlgorithm.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.simplejwt.constants;
19 |
20 | import java.util.List;
21 |
22 | /**
23 | * The {@code TokenAlgorithm} enum class defines the algorithms that can be
24 | * used for signing and verifying JSON Web Tokens (JWT). JWT allows various
25 | * cryptographic algorithms to be used for secure token generation and
26 | * validation. This enum provides a list of supported algorithms to ensure
27 | * consistent usage and avoid potential security issues.
28 | *
29 | * Supported Algorithms:
30 | *
31 | * - {@link TokenAlgorithm#HS256}: HMAC SHA-256
32 | * - {@link TokenAlgorithm#HS384}: HMAC SHA-384
33 | * - {@link TokenAlgorithm#HS512}: HMAC SHA-512
34 | * - {@link TokenAlgorithm#RS256}: RSA PKCS#1 v1.5 with SHA-256
35 | * - {@link TokenAlgorithm#RS384}: RSA PKCS#1 v1.5 with SHA-384
36 | * - {@link TokenAlgorithm#RS512}: RSA PKCS#1 v1.5 with SHA-512
37 | * - {@link TokenAlgorithm#ES256}: ECDSA with SHA-256
38 | * - {@link TokenAlgorithm#ES384}: ECDSA with SHA-384
39 | * - {@link TokenAlgorithm#ES512}: ECDSA with SHA-512
40 | *
41 | *
42 | * @author Zihlu Wang
43 | * @version 1.1.0
44 | * @since 1.0.0
45 | */
46 | public enum TokenAlgorithm {
47 |
48 | /**
49 | * HMAC using SHA-256
50 | */
51 | HS256,
52 |
53 | /**
54 | * HMAC using SHA-384
55 | */
56 | HS384,
57 |
58 | /**
59 | * HMAC using SHA-512
60 | */
61 | HS512,
62 |
63 | /**
64 | * RSASSA-PKCS-v1_5 using SHA-256
65 | */
66 | RS256,
67 |
68 | /**
69 | * RSASSA-PKCS-v1_5 using SHA-384
70 | */
71 | RS384,
72 |
73 | /**
74 | * RSASSA-PKCS-v1_5 using SHA-512
75 | */
76 | RS512,
77 |
78 | /**
79 | * ECDSA using P-256 and SHA-256
80 | */
81 | ES256,
82 |
83 | /**
84 | * ECDSA using P-384 and SHA-384
85 | */
86 | ES384,
87 |
88 | /**
89 | * ECDSA using P-521 and SHA-512
90 | */
91 | ES512,
92 | ;
93 |
94 | /**
95 | * HMAC-based algorithms.
96 | */
97 | public static final List HMAC_ALGORITHMS = List.of(
98 | TokenAlgorithm.HS256, TokenAlgorithm.HS384, TokenAlgorithm.HS512
99 | );
100 |
101 | /**
102 | * ECDSA-based algorithms.
103 | */
104 | public static final List ECDSA_ALGORITHMS = List.of(
105 | TokenAlgorithm.ES256, TokenAlgorithm.ES384, TokenAlgorithm.ES512
106 | );
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/simple-jwt-facade/src/main/java/com/onixbyte/simplejwt/constants/TokenDataType.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.simplejwt.constants;
19 |
20 | /**
21 | * The base data types used to process enum data.
22 | *
23 | * @author Zihlu Wang
24 | */
25 | public enum TokenDataType {
26 |
27 | /**
28 | * Marks enumeration being processed as Boolean.
29 | */
30 | BOOLEAN(Boolean.class),
31 |
32 | /**
33 | * Marks enumeration being processed as Double.
34 | */
35 | DOUBLE(Long.class),
36 |
37 | /**
38 | * Marks enumeration being processed as Float.
39 | */
40 | FLOAT(Float.class),
41 |
42 | /**
43 | * Marks enumeration being processed as Integer.
44 | */
45 | INTEGER(Integer.class),
46 |
47 | /**
48 | * Marks enumeration being processed as Long.
49 | */
50 | LONG(Long.class),
51 |
52 | /**
53 | * Marks enumeration being processed as String.
54 | */
55 | STRING(String.class),
56 | ;
57 |
58 | /**
59 | * The mapped class to this mark.
60 | */
61 | private final Class> mappedClass;
62 |
63 | /**
64 | * Create a TokenDataType with a mapped class.
65 | */
66 | TokenDataType(Class> mappedClass) {
67 | this.mappedClass = mappedClass;
68 | }
69 |
70 | /**
71 | * Return the target mapped class.
72 | *
73 | * @return mapped class
74 | */
75 | public Class> getMappedClass() {
76 | return mappedClass;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/simple-jwt-facade/src/main/java/com/onixbyte/simplejwt/exceptions/IllegalKeyPairException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.simplejwt.exceptions;
19 |
20 | /**
21 | * {@link IllegalKeyPairException} indicates an exception that the key pair is invalid.
22 | *
23 | * @author zihluwang
24 | * @version 1.6.0
25 | */
26 | public class IllegalKeyPairException extends RuntimeException {
27 |
28 | /**
29 | * Create a default exception instance.
30 | */
31 | public IllegalKeyPairException() {
32 | }
33 |
34 | /**
35 | * Create an exception instance with specific message.
36 | *
37 | * @param message the message of the exception
38 | */
39 | public IllegalKeyPairException(String message) {
40 | super(message);
41 | }
42 |
43 | /**
44 | * Create an exception instance with specific message and cause.
45 | *
46 | * @param message the message of the exception
47 | * @param cause the cause of the exception
48 | */
49 | public IllegalKeyPairException(String message, Throwable cause) {
50 | super(message, cause);
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/simple-jwt-facade/src/main/java/com/onixbyte/simplejwt/exceptions/IllegalSecretException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.simplejwt.exceptions;
19 |
20 | /**
21 | * {@link IllegalKeyPairException} indicates the secret to sign a JWT is illegal.
22 | *
23 | * @author zihluwang
24 | * @version 1.6.0
25 | * @since 1.6.0
26 | */
27 | public class IllegalSecretException extends RuntimeException {
28 |
29 | /**
30 | * Create a default exception instance.
31 | */
32 | public IllegalSecretException() {
33 | }
34 |
35 | /**
36 | * Create an exception instance with specific message.
37 | *
38 | * @param message the message of the exception
39 | */
40 | public IllegalSecretException(String message) {
41 | super(message);
42 | }
43 |
44 | /**
45 | * Create an exception instance with specific message and the cause of this exception.
46 | *
47 | * @param message the message of the exception
48 | * @param cause the cause of the exception
49 | */
50 | public IllegalSecretException(String message, Throwable cause) {
51 | super(message, cause);
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/simple-jwt-facade/src/main/java/com/onixbyte/simplejwt/exceptions/UnsupportedAlgorithmException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.simplejwt.exceptions;
19 |
20 | import com.onixbyte.simplejwt.TokenResolver;
21 |
22 | /**
23 | * This {@code UnsupportedAlgorithmException} represents the given
24 | * algorithm is not supported by {@link
25 | * TokenResolver} yet.
26 | *
27 | * If you want the supports to an unsupported algorithm, you could
28 | *
29 | * - Commit an issue at GitHub Issues or;
30 | * - Communicate with us on Discord Community.
31 | *
32 | *
33 | * @author Zihlu Wang
34 | * @version 1.1.0
35 | * @since 1.0.0
36 | */
37 | public class UnsupportedAlgorithmException extends RuntimeException {
38 |
39 | /**
40 | * Constructs a new {@code UnsupportedAlgorithmException} with {@code null}
41 | * as its detail message. The cause is not initialized, and may
42 | * subsequently be initialized by a call to {@link #initCause}.
43 | */
44 | public UnsupportedAlgorithmException() {
45 | }
46 |
47 | /**
48 | * Constructs a new {@code UnsupportedAlgorithmException} with the
49 | * specified detail message. The cause is not initialized, and may
50 | * subsequently be initialized by a call to {@link #initCause}.
51 | *
52 | * @param message the detail message. The detail message is saved for
53 | * later retrieval by the {@link #getMessage()} method.
54 | */
55 | public UnsupportedAlgorithmException(String message) {
56 | super(message);
57 | }
58 |
59 | /**
60 | * Constructs a new {@code UnsupportedAlgorithmException} with the
61 | * specified detail message and cause.
62 | *
63 | * @param message the detail message (which is saved for later retrieval
64 | * by the {@link #getMessage()} method).
65 | * @param cause the cause (which is saved for later retrieval by the
66 | * {@link #getCause()} method). (A {@code null} value is
67 | * permitted, and indicates that the cause is nonexistent or
68 | * unknown.)
69 | * @since 1.4
70 | */
71 | public UnsupportedAlgorithmException(String message, Throwable cause) {
72 | super(message, cause);
73 | }
74 |
75 | /**
76 | * Constructs a new {@code UnsupportedAlgorithmException} with the
77 | * specified cause and a detail message of
78 | * {@code (cause==null ? null : cause.toString())} (which typically
79 | * contains the class and detail message of {@code cause}). This
80 | * constructor is useful for runtime exceptions that are little more
81 | * than wrappers for other throwable.
82 | *
83 | * @param cause the cause (which is saved for later retrieval by the
84 | * {@link #getCause()} method). (A {@code null} value is
85 | * permitted, and indicates that the cause is nonexistent or
86 | * unknown.)
87 | * @since 1.4
88 | */
89 | public UnsupportedAlgorithmException(Throwable cause) {
90 | super(cause);
91 | }
92 |
93 | /**
94 | * Constructs a new {@code UnsupportedAlgorithmException} with the
95 | * specified detail message, cause, suppression enabled or disabled, and
96 | * writable stack trace enabled or disabled.
97 | *
98 | * @param message the detail message.
99 | * @param cause the cause (A {@code null} value is permitted,
100 | * and indicates that the cause is nonexistent or
101 | * unknown.)
102 | * @param enableSuppression whether suppression is enabled or disabled
103 | * @param writableStackTrace whether the stack trace should be writable
104 | * @since 1.0.0
105 | */
106 | public UnsupportedAlgorithmException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
107 | super(message, cause, enableSuppression, writableStackTrace);
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/simple-jwt-facade/src/main/java/com/onixbyte/simplejwt/exceptions/WeakSecretException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.simplejwt.exceptions;
19 |
20 | /**
21 | * {@code WeakSecretException} represents that your secret is too weak to be
22 | * used in signing JWTs.
23 | *
24 | * {@code WeakSecretException} will only appears that if you are using the
25 | * implementation module {@code cn.org.codecrafters:simple-jwt-jjwt} due to
26 | * it is implemented by {@code io.jsonwebtoken:jjwt}.
27 | *
28 | * @author Zihlu Wang
29 | * @version 1.1.0
30 | * @since 1.0.0
31 | */
32 | public class WeakSecretException extends RuntimeException {
33 |
34 | /**
35 | * Constructs a new {@code WeakSecretException} with {@code null} as its
36 | * detail message. The cause is not initialized, and may subsequently be
37 | * initialized by a call to {@link #initCause}.
38 | */
39 | public WeakSecretException() {
40 | }
41 |
42 | /**
43 | * Constructs a new {@code WeakSecretException} with the specified detail
44 | * message. The cause is not initialized, and may subsequently be
45 | * initialized by a call to {@link #initCause}.
46 | *
47 | * @param message the detail message. The detail message is saved for
48 | * later retrieval by the {@link #getMessage()} method.
49 | */
50 | public WeakSecretException(String message) {
51 | super(message);
52 | }
53 |
54 | /**
55 | * Constructs a new {@code WeakSecretException} with the specified detail
56 | * message and cause.
57 | *
58 | * Note that the detail message associated with {@code cause} is not
59 | * automatically incorporated in this runtime exception's detail message.
60 | *
61 | * @param message the detail message (which is saved for later retrieval
62 | * by the {@link #getMessage()} method).
63 | * @param cause the cause (which is saved for later retrieval by the
64 | * {@link #getCause()} method). (A {@code null} value is
65 | * permitted, and indicates that the cause is nonexistent or
66 | * unknown.)
67 | * @since 1.0.0
68 | */
69 | public WeakSecretException(String message, Throwable cause) {
70 | super(message, cause);
71 | }
72 |
73 | /**
74 | * Constructs a new {@code WeakSecretException} with the specified cause
75 | * and a detail message of {@code (cause==null ? null : cause.toString())}
76 | * (which typically contains the class and detail message of
77 | * {@code cause}). This constructor is useful for runtime exceptions that
78 | * are little more than wrappers for other throwable.
79 | *
80 | * @param cause the cause (which is saved for later retrieval by the
81 | * {@link #getCause()} method). (A {@code null} value is
82 | * permitted, and indicates that the cause is nonexistent or
83 | * unknown.)
84 | * @since 1.0.0
85 | */
86 | public WeakSecretException(Throwable cause) {
87 | super(cause);
88 | }
89 |
90 | /**
91 | * Constructs a new {@code WeakSecretException} with the specified detail
92 | * message, cause, suppression enabled or disabled, and writable
93 | * stack trace enabled or disabled.
94 | *
95 | * @param message the detail message.
96 | * @param cause the cause. (A {@code null} value is permitted,
97 | * and indicates that the cause is nonexistent or
98 | * unknown.)
99 | * @param enableSuppression whether suppression is enabled or disabled
100 | * @param writableStackTrace whether the stack trace should be writable
101 | * @since 1.0.0
102 | */
103 | public WeakSecretException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
104 | super(message, cause, enableSuppression, writableStackTrace);
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/simple-jwt-facade/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | ${COLOURFUL_OUTPUT}
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/simple-jwt-spring-boot-starter/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | import java.net.URI
18 |
19 | plugins {
20 | java
21 | id("java-library")
22 | id("maven-publish")
23 | id("signing")
24 | }
25 |
26 | val artefactVersion: String by project
27 | val projectUrl: String by project
28 | val projectGithubUrl: String by project
29 | val licenseName: String by project
30 | val licenseUrl: String by project
31 |
32 | group = "com.onixbyte"
33 | version = artefactVersion
34 |
35 | repositories {
36 | mavenCentral()
37 | }
38 |
39 | java {
40 | sourceCompatibility = JavaVersion.VERSION_17
41 | targetCompatibility = JavaVersion.VERSION_17
42 | withSourcesJar()
43 | withJavadocJar()
44 | }
45 |
46 | tasks.withType {
47 | options.encoding = "UTF-8"
48 | }
49 |
50 | tasks.withType {
51 | exclude("logback.xml")
52 | }
53 |
54 | dependencies {
55 | compileOnly(libs.slf4j)
56 | implementation(libs.logback)
57 |
58 | api(project(":guid"))
59 | api(project(":simple-jwt-facade"))
60 | api(project(":simple-jwt-authzero"))
61 | implementation(libs.springBoot.autoconfigure)
62 | implementation(libs.springBoot.starter.logging)
63 | implementation(libs.springBoot.configurationProcessor)
64 | annotationProcessor(libs.springBoot.configurationProcessor)
65 |
66 | testImplementation(platform(libs.junit.bom))
67 | testImplementation(libs.junit.jupiter)
68 | testImplementation(libs.springBoot.starter.test)
69 | }
70 |
71 | java {
72 | sourceCompatibility = JavaVersion.VERSION_17
73 | targetCompatibility = JavaVersion.VERSION_17
74 | withSourcesJar()
75 | withJavadocJar()
76 | }
77 |
78 | tasks.test {
79 | useJUnitPlatform()
80 | }
81 |
82 | publishing {
83 | publications {
84 | create("simpleJwtSpringBootStarter") {
85 | groupId = group.toString()
86 | artifactId = "simple-jwt-spring-boot-starter"
87 | version = artefactVersion
88 |
89 | pom {
90 | name = "Simple JWT :: Spring Boot Starter"
91 | description = "Simple JWT all-in-one package for Spring Boot."
92 | url = projectUrl
93 |
94 | licenses {
95 | license {
96 | name = licenseName
97 | url = licenseUrl
98 | }
99 | }
100 |
101 | scm {
102 | connection = "scm:git:git://github.com:OnixByte/JDevKit.git"
103 | developerConnection = "scm:git:git://github.com:OnixByte/JDevKit.git"
104 | url = projectGithubUrl
105 | }
106 |
107 | developers {
108 | developer {
109 | id = "zihluwang"
110 | name = "Zihlu Wang"
111 | email = "really@zihlu.wang"
112 | timezone = "Asia/Hong_Kong"
113 | }
114 |
115 | developer {
116 | id = "siujamo"
117 | name = "Siu Jam'o"
118 | email = "jamo.siu@outlook.com"
119 | timezone = "Asia/Shanghai"
120 | }
121 | }
122 | }
123 |
124 | from(components["java"])
125 |
126 | signing {
127 | sign(publishing.publications["simpleJwtSpringBootStarter"])
128 | }
129 | }
130 |
131 | repositories {
132 | maven {
133 | name = "sonatypeNexus"
134 | url = URI(providers.gradleProperty("repo.maven-central.host").get())
135 | credentials {
136 | username = providers.gradleProperty("repo.maven-central.username").get()
137 | password = providers.gradleProperty("repo.maven-central.password").get()
138 | }
139 | }
140 | }
141 | }
142 | }
--------------------------------------------------------------------------------
/simple-jwt-spring-boot-starter/src/main/java/com/onixbyte/simplejwt/autoconfiguration/GuidAutoConfiguration.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.simplejwt.autoconfiguration;
19 |
20 | import com.onixbyte.guid.GuidCreator;
21 | import com.onixbyte.simplejwt.autoconfiguration.conditions.GuidCreatorCondition;
22 | import org.slf4j.Logger;
23 | import org.slf4j.LoggerFactory;
24 | import org.springframework.boot.autoconfigure.AutoConfiguration;
25 | import org.springframework.context.annotation.Bean;
26 | import org.springframework.context.annotation.Conditional;
27 |
28 | import java.util.UUID;
29 |
30 | /**
31 | * Autoconfiguration for injecting a {@link GuidCreator} for generating jwt id.
32 | *
33 | * @author Zihlu Wang
34 | * @version 1.1.0
35 | * @since 1.0.0
36 | */
37 | @AutoConfiguration
38 | public class GuidAutoConfiguration {
39 |
40 | private final static Logger log = LoggerFactory.getLogger(GuidAutoConfiguration.class);
41 |
42 | /**
43 | * Default constructor.
44 | */
45 | public GuidAutoConfiguration() {
46 | }
47 |
48 | /**
49 | * Create a default {@code jtiCreator} with UUID.
50 | *
51 | * @return UUID creator
52 | */
53 | @Bean(name = "jtiCreator")
54 | @Conditional(GuidCreatorCondition.class)
55 | public GuidCreator> jtiCreator() {
56 | return UUID::randomUUID;
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/simple-jwt-spring-boot-starter/src/main/java/com/onixbyte/simplejwt/autoconfiguration/conditions/GuidCreatorCondition.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.simplejwt.autoconfiguration.conditions;
19 |
20 | import com.onixbyte.guid.GuidCreator;
21 | import org.slf4j.Logger;
22 | import org.slf4j.LoggerFactory;
23 | import org.springframework.context.annotation.Condition;
24 | import org.springframework.context.annotation.ConditionContext;
25 | import org.springframework.core.type.AnnotatedTypeMetadata;
26 |
27 | import java.util.Objects;
28 |
29 | /**
30 | * The conditions to create bean {@code jtiCreator}.
31 | *
32 | * @author Zihlu Wang
33 | * @version 1.1.0
34 | * @since 1.0.0
35 | */
36 | public class GuidCreatorCondition implements Condition {
37 |
38 | private final static Logger log = LoggerFactory.getLogger(GuidCreatorCondition.class);
39 |
40 | /**
41 | * Default constructor.
42 | */
43 | public GuidCreatorCondition() {
44 | }
45 |
46 | /**
47 | * The condition to create bean {@code jtiCreator}.
48 | *
49 | * If Spring does not have a bean of type {@link GuidCreator} named {@code jtiCreator} in the
50 | * application context, then create {@code jtiCreator}.
51 | *
52 | * @param context the spring application context
53 | * @param metadata the metadata of the {@link org.springframework.core.type.AnnotationMetadata
54 | * class} or {@link org.springframework.core.type.MethodMetadata method}
55 | * being checked
56 | */
57 | @Override
58 | public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
59 | final var beanFactory = Objects.requireNonNull(context.getBeanFactory());
60 | var isContainJtiCreator = beanFactory.containsBean("jtiCreator");
61 | if (isContainJtiCreator) {
62 | return !(beanFactory.getBean("jtiCreator") instanceof GuidCreator>);
63 | }
64 | return true;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/simple-jwt-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports:
--------------------------------------------------------------------------------
1 | com.onixbyte.simplejwt.autoconfiguration.GuidAutoConfiguration
2 | com.onixbyte.simplejwt.autoconfiguration.AuthzeroTokenResolverAutoConfiguration
--------------------------------------------------------------------------------
/simple-jwt-spring-boot-starter/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | ${COLOURFUL_OUTPUT}
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/simple-serial-spring-boot-starter/README.md:
--------------------------------------------------------------------------------
1 | # Simple Serial
2 |
3 | > Thanks to [@siujamo](https://github.com/siujamo)'s donation.
4 |
5 | Simple Serial reuses the configuration of Redis connections to provide am easy-to-use serial
6 | service.
7 |
8 | ## Configuration
9 |
10 | Simple Serial reused the redis configuration of Spring Boot to provide redis support for the
11 | service.
12 |
13 | Besides, **Simple Serial** provides a configuration property `onixbyte.serial.start-serial` to
14 | specify the start value of a serial, and default to `0`.
--------------------------------------------------------------------------------
/simple-serial-spring-boot-starter/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | import java.net.URI
18 |
19 | plugins {
20 | java
21 | id("java-library")
22 | id("maven-publish")
23 | id("signing")
24 | }
25 |
26 | val artefactVersion: String by project
27 | val projectUrl: String by project
28 | val projectGithubUrl: String by project
29 | val licenseName: String by project
30 | val licenseUrl: String by project
31 |
32 | group = "com.onixbyte"
33 | version = artefactVersion
34 |
35 | repositories {
36 | mavenCentral()
37 | }
38 |
39 | java {
40 | sourceCompatibility = JavaVersion.VERSION_17
41 | targetCompatibility = JavaVersion.VERSION_17
42 | withSourcesJar()
43 | withJavadocJar()
44 | }
45 |
46 | tasks.withType {
47 | options.encoding = "UTF-8"
48 | }
49 |
50 | tasks.withType {
51 | exclude("logback.xml")
52 | }
53 |
54 | dependencies {
55 | compileOnly(libs.slf4j)
56 | implementation(libs.logback)
57 |
58 | implementation(libs.jackson.databind)
59 | implementation(libs.springBoot.autoconfigure)
60 | implementation(libs.springBoot.starter.logging)
61 | implementation(libs.springBoot.configurationProcessor)
62 | implementation(libs.springBoot.starter.redis)
63 | annotationProcessor(libs.springBoot.configurationProcessor)
64 |
65 | testImplementation(platform(libs.junit.bom))
66 | testImplementation(libs.junit.jupiter)
67 | testImplementation(libs.springBoot.starter.test)
68 | }
69 |
70 | tasks.test {
71 | useJUnitPlatform()
72 | }
73 |
74 | publishing {
75 | publications {
76 | create("simpleSerialSpringBootStarter") {
77 | groupId = group.toString()
78 | artifactId = "simple-serial-spring-boot-starter"
79 | version = artefactVersion
80 |
81 | pom {
82 | name = "Simple Serial :: Spring Boot Starter"
83 | description = "A Redis based easy-to-use serial service."
84 | url = projectUrl
85 |
86 | licenses {
87 | license {
88 | name = licenseName
89 | url = licenseUrl
90 | }
91 | }
92 |
93 | scm {
94 | connection = "scm:git:git://github.com:OnixByte/JDevKit.git"
95 | developerConnection = "scm:git:git://github.com:OnixByte/JDevKit.git"
96 | url = projectGithubUrl
97 | }
98 |
99 | developers {
100 | developer {
101 | id = "zihluwang"
102 | name = "Zihlu Wang"
103 | email = "really@zihlu.wang"
104 | timezone = "Asia/Hong_Kong"
105 | }
106 |
107 | developer {
108 | id = "siujamo"
109 | name = "Siu Jam'o"
110 | email = "jamo.siu@outlook.com"
111 | timezone = "Asia/Shanghai"
112 | }
113 | }
114 | }
115 |
116 | from(components["java"])
117 |
118 | signing {
119 | sign(publishing.publications["simpleSerialSpringBootStarter"])
120 | }
121 | }
122 |
123 | repositories {
124 | maven {
125 | name = "sonatypeNexus"
126 | url = URI(providers.gradleProperty("repo.maven-central.host").get())
127 | credentials {
128 | username = providers.gradleProperty("repo.maven-central.username").get()
129 | password = providers.gradleProperty("repo.maven-central.password").get()
130 | }
131 | }
132 | }
133 | }
134 | }
--------------------------------------------------------------------------------
/simple-serial-spring-boot-starter/src/main/java/com/onixbyte/serial/RedisConfig.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.serial;
19 |
20 | import org.springframework.boot.autoconfigure.AutoConfiguration;
21 | import org.springframework.context.annotation.Bean;
22 | import org.springframework.data.redis.connection.RedisConnectionFactory;
23 | import org.springframework.data.redis.core.RedisTemplate;
24 | import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
25 | import org.springframework.data.redis.serializer.RedisSerializer;
26 |
27 | /**
28 | * Redis Configuration provides redis templates for operations to serial.
29 | *
30 | * @author siujamo
31 | */
32 | @AutoConfiguration
33 | public class RedisConfig {
34 |
35 | /**
36 | * Redis auto configuration.
37 | */
38 | public RedisConfig() {
39 | }
40 |
41 | /**
42 | * RedisTemplate for serial service.
43 | *
44 | * @param redisConnectionFactory redis connection factory
45 | * @return a configured redis template for serial service
46 | */
47 | @Bean
48 | public RedisTemplate serialRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
49 | var redisTemplate = new RedisTemplate();
50 | redisTemplate.setConnectionFactory(redisConnectionFactory);
51 | redisTemplate.setKeySerializer(RedisSerializer.string());
52 | redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(Long.class));
53 | redisTemplate.afterPropertiesSet();
54 | return redisTemplate;
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/simple-serial-spring-boot-starter/src/main/java/com/onixbyte/serial/SerialService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.serial;
19 |
20 | import com.onixbyte.serial.properties.SerialProperties;
21 | import org.slf4j.Logger;
22 | import org.slf4j.LoggerFactory;
23 | import org.springframework.beans.factory.annotation.Value;
24 | import org.springframework.boot.context.properties.EnableConfigurationProperties;
25 | import org.springframework.data.redis.core.RedisTemplate;
26 | import org.springframework.stereotype.Service;
27 |
28 | import java.util.Optional;
29 |
30 | /**
31 | * {@code SerialService} provides simple serial operations.
32 | *
33 | * @author siujamo
34 | */
35 | @Service
36 | @EnableConfigurationProperties(SerialProperties.class)
37 | public class SerialService {
38 |
39 | private static final Logger log = LoggerFactory.getLogger(SerialService.class);
40 |
41 | private final String appName;
42 | private final RedisTemplate serialRedisTemplate;
43 | private final SerialProperties serialProperties;
44 |
45 | /**
46 | * Default constructor.
47 | *
48 | * @param appName the name of this application
49 | * @param serialRedisTemplate serial redis template
50 | * @param serialProperties serial properties
51 | */
52 | public SerialService(@Value("${spring.application.name}") String appName,
53 | RedisTemplate serialRedisTemplate,
54 | SerialProperties serialProperties) {
55 | this.appName = appName;
56 | this.serialRedisTemplate = serialRedisTemplate;
57 | this.serialProperties = serialProperties;
58 | }
59 |
60 | /**
61 | * Build a serial key.
62 | *
63 | * @param tag tag of the serial
64 | * @return key of a serial
65 | */
66 | public String buildKey(String tag) {
67 | return appName + ":serial:" + tag;
68 | }
69 |
70 | /**
71 | * Get the next available serial for specific tag.
72 | *
73 | * @param tag tag of the serial
74 | * @return next available serial
75 | */
76 | public Long nextSerial(String tag) {
77 | var key = buildKey(tag);
78 | var next = Optional.ofNullable(serialRedisTemplate.opsForValue().get(key))
79 | .orElse(serialProperties.getStartSerial());
80 | serialRedisTemplate.opsForValue().set(key, next + 1);
81 | return next;
82 | }
83 |
84 | /**
85 | * Reset all serial values.
86 | */
87 | public void reset() {
88 | var keys = serialRedisTemplate.keys(buildKey("*"));
89 | var startSerial = serialProperties.getStartSerial();
90 |
91 | if (!keys.isEmpty()) {
92 | for (var key : keys) {
93 | serialRedisTemplate.opsForValue().set(key, startSerial);
94 | log.debug("Serial {} has been reset to {}", key, startSerial);
95 | }
96 | }
97 | }
98 |
99 | }
100 |
--------------------------------------------------------------------------------
/simple-serial-spring-boot-starter/src/main/java/com/onixbyte/serial/properties/SerialProperties.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2024-2025 OnixByte.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | *
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.onixbyte.serial.properties;
19 |
20 | import org.springframework.boot.context.properties.ConfigurationProperties;
21 |
22 | /**
23 | * SerialProperties can modify the start value of a serial.
24 | *
25 | * @author siujamo
26 | */
27 | @ConfigurationProperties(prefix = "onixbyte.serial")
28 | public class SerialProperties {
29 |
30 | /**
31 | * The start of the serial, default to 0.
32 | */
33 | private Long startSerial = 0L;
34 |
35 | /**
36 | * Get the start of the serial.
37 | *
38 | * @return start of the serial
39 | */
40 | public Long getStartSerial() {
41 | return startSerial;
42 | }
43 |
44 | /**
45 | * Set the start of the serial.
46 | *
47 | * @param startSerial start of the serial
48 | */
49 | public void setStartSerial(Long startSerial) {
50 | this.startSerial = startSerial;
51 | }
52 |
53 | /**
54 | * Default constructor.
55 | */
56 | public SerialProperties() {
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/simple-serial-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright (C) 2024-2025 OnixByte.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | #
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 | #
17 |
18 | com.onixbyte.serial.RedisConfig
19 | com.onixbyte.serial.SerialService
--------------------------------------------------------------------------------
/simple-serial-spring-boot-starter/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | ${COLOURFUL_OUTPUT}
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------