├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── github-packages-publish.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── build.gradle.kts ├── devkit-bom ├── README.md └── build.gradle.kts ├── devkit-core ├── README.md ├── build.gradle.kts └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── onixbyte │ │ │ └── devkit │ │ │ └── core │ │ │ └── exceptions │ │ │ └── NotImplementedException.java │ └── resources │ │ └── logback.xml │ └── test │ └── java │ └── com │ └── onixbyte │ └── devkit │ └── core │ └── exceptions │ └── NotImplementationExceptionTest.java ├── devkit-utils ├── README.md ├── build.gradle.kts └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── onixbyte │ │ │ └── devkit │ │ │ └── utils │ │ │ ├── AesUtil.java │ │ │ ├── Base64Util.java │ │ │ ├── BoolUtil.java │ │ │ ├── BranchUtil.java │ │ │ ├── CollectionUtil.java │ │ │ ├── HashUtil.java │ │ │ ├── MapUtil.java │ │ │ ├── ObjectMapAdapter.java │ │ │ └── RangeUtil.java │ └── resources │ │ └── logback.xml │ └── test │ └── java │ └── com │ └── onixbyte │ └── devkit │ └── utils │ ├── AesUtilTest.java │ ├── Base64UtilTest.java │ ├── BoolUtilTest.java │ ├── BranchUtilTest.java │ ├── CollectionUtilTest.java │ ├── HashUtilTest.java │ └── RangeUtilTest.java ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── guid ├── README.md ├── build.gradle.kts └── src │ └── main │ ├── java │ └── com │ │ └── onixbyte │ │ └── guid │ │ ├── GuidCreator.java │ │ ├── exceptions │ │ └── TimingException.java │ │ └── impl │ │ ├── SequentialUuidCreator.java │ │ └── SnowflakeGuidCreator.java │ └── resources │ └── logback.xml ├── key-pair-loader ├── README.md ├── build.gradle.kts └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── onixbyte │ │ │ └── security │ │ │ ├── KeyLoader.java │ │ │ ├── exception │ │ │ └── KeyLoadingException.java │ │ │ └── impl │ │ │ ├── EcKeyLoader.java │ │ │ └── RsaKeyLoader.java │ └── resources │ │ └── logback.xml │ └── test │ └── resources │ ├── ec_private_key.pem │ ├── ec_public_key.pem │ ├── rsa_private_key.pem │ └── rsa_public_key.pem ├── map-util-unsafe ├── README.md ├── build.gradle.kts └── src │ └── main │ ├── java │ └── com │ │ └── onixbyte │ │ └── devkit │ │ └── utils │ │ └── unsafe │ │ └── ReflectMapUtil.java │ └── resources │ └── logback.xml ├── num4j ├── README.md ├── build.gradle.kts └── src │ └── main │ ├── java │ └── com │ │ └── onixbyte │ │ └── nums │ │ ├── ChainedCalcUtil.java │ │ ├── PercentileCalculator.java │ │ └── model │ │ └── QuartileBounds.java │ └── resources │ └── logback.xml ├── property-guard-spring-boot-starter ├── README.md ├── build.gradle.kts └── src │ └── main │ ├── java │ └── com │ │ └── onixbyte │ │ └── propertyguard │ │ └── autoconfiguration │ │ └── PropertyGuard.java │ └── resources │ ├── META-INF │ └── spring.factories │ └── logback.xml ├── settings.gradle.kts ├── simple-jwt-authzero ├── README.md ├── build.gradle.kts └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── onixbyte │ │ │ └── simplejwt │ │ │ └── authzero │ │ │ └── AuthzeroTokenResolver.java │ └── resources │ │ └── logback.xml │ └── test │ └── java │ └── com │ └── onixbyte │ └── simplejwt │ └── authzero │ └── test │ └── TestAuthzeroTokenResolver.java ├── simple-jwt-facade ├── README.md ├── build.gradle.kts └── src │ └── main │ ├── java │ └── com │ │ └── onixbyte │ │ └── simplejwt │ │ ├── SecretCreator.java │ │ ├── TokenPayload.java │ │ ├── TokenResolver.java │ │ ├── annotations │ │ ├── ExcludeFromPayload.java │ │ └── TokenEnum.java │ │ ├── constants │ │ ├── PredefinedKeys.java │ │ ├── TokenAlgorithm.java │ │ └── TokenDataType.java │ │ └── exceptions │ │ ├── IllegalKeyPairException.java │ │ ├── IllegalSecretException.java │ │ ├── UnsupportedAlgorithmException.java │ │ └── WeakSecretException.java │ └── resources │ └── logback.xml ├── simple-jwt-spring-boot-starter ├── README.md ├── build.gradle.kts └── src │ └── main │ ├── java │ └── com │ │ └── onixbyte │ │ └── simplejwt │ │ └── autoconfiguration │ │ ├── AuthzeroTokenResolverAutoConfiguration.java │ │ ├── GuidAutoConfiguration.java │ │ ├── conditions │ │ └── GuidCreatorCondition.java │ │ └── properties │ │ └── SimpleJwtProperties.java │ └── resources │ ├── META-INF │ └── spring │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ └── logback.xml └── simple-serial-spring-boot-starter ├── README.md ├── build.gradle.kts └── src └── main ├── java └── com │ └── onixbyte │ └── serial │ ├── RedisConfig.java │ ├── SerialService.java │ └── properties │ └── SerialProperties.java └── resources ├── META-INF └── spring │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports └── logback.xml /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: zihluwang, theodorehills 7 | 8 | --- 9 | 10 | **Which module creates this bug** 11 | - [ ] `devkit-core` 12 | - [ ] `devkit-utils` 13 | - [ ] `guid` 14 | - [ ] `webcal` 15 | - [ ] `simple-jwt-facade` 16 | - [ ] `simple-jwt-authzero` 17 | - [ ] `simple-jwt-jjwt` 18 | - [ ] `simple-jwt-spring-boot-starter` 19 | 20 | **Describe the bug** 21 | A clear and concise description of what the bug is. 22 | 23 | **To Reproduce** 24 | Steps to reproduce the behavior: 25 | 1. Go to '...' 26 | 2. Click on '....' 27 | 3. Scroll down to '....' 28 | 4. See error 29 | 30 | **Expected behavior** 31 | A clear and concise description of what you expected to happen. 32 | 33 | **Screenshots** 34 | If applicable, add screenshots to help explain your problem. 35 | 36 | **Developing Environment:** 37 | - OS: [e.g. Windows, macOS, Linux, Ubuntu] 38 | - Browser [e.g. chrome, safari] 39 | - Version [e.g. 22] 40 | - Java Version: [e.g. Amazon Corretto 17, Oracle 17, Azul Zulu 17, Microsoft Build of OpenJDK 17] 41 | 42 | **Additional context** 43 | Add any other context about the problem here. 44 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[FEATURE REQUEST]" 5 | labels: feature request 6 | assignees: zihluwang, theodorehills 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/github-packages-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # This workflow will build a package using Gradle and then publish it to GitHub packages when a release is created 6 | # For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#Publishing-using-gradle 7 | 8 | name: Publish Packages to GitHub Packages with Gradle 9 | 10 | on: 11 | release: 12 | types: 13 | - published 14 | 15 | jobs: 16 | build: 17 | name: Build and Publish 18 | runs-on: ubuntu-latest 19 | permissions: 20 | contents: read 21 | packages: write 22 | 23 | steps: 24 | - name: Checkout 25 | uses: actions/checkout@v4 26 | 27 | - name: Setup GPG TTY 28 | run: export GPG_TTY=$(tty) 29 | 30 | - name: Import PGP Private Key 31 | uses: crazy-max/ghaction-import-gpg@v6.1.0 32 | with: 33 | gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} 34 | passphrase: ${{ secrets.GPG_PASSPHRASE }} 35 | trust_level: 5 36 | 37 | - name: Creating PGP Ring Key 38 | run: | 39 | mkdir -p ~/.gnupg 40 | echo ${{ secrets.GPG_PASSPHRASE }} | gpg --batch --yes --pinentry-mode loopback --passphrase-fd 0 --export-secret-keys -o ~/.gnupg/gpg_key.ring 41 | 42 | - name: Restore gradle.properties 43 | env: 44 | GRADLE_PROPERTIES: ${{ secrets.GRADLE_PROPERTIES }} 45 | shell: bash 46 | run: | 47 | mkdir -p ~/.gradle/ 48 | echo "GRADLE_USER_HOME=${HOME}/.gradle" >> $GITHUB_ENV 49 | echo "${GRADLE_PROPERTIES}" > ~/.gradle/gradle.properties 50 | 51 | - name: Set up JDK 17 52 | uses: actions/setup-java@v4 53 | with: 54 | java-version: "17" 55 | distribution: "corretto" 56 | 57 | - name: Setup Gradle 58 | uses: gradle/actions/setup-gradle@v4 59 | 60 | - name: Grant Execution Authority to Gradlew 61 | run: chmod +x ./gradlew 62 | 63 | - name: Build with Gradle 64 | # Overwrite artefactVersion with tag name 65 | run: ./gradlew build -PartefactVersion=${{ github.event.release.tag_name }} 66 | 67 | - name: List Output Items 68 | run: ls -l ./**/build/libs 69 | 70 | - name: Publish to Maven Central 71 | run: ./gradlew publish 72 | 73 | - name: Create Deployment on Central Publisher Portal 74 | run: | 75 | curl --fail -X 'POST' \ 76 | 'https://ossrh-staging-api.central.sonatype.com/manual/upload/defaultRepository/com.onixbyte?publishing_type=user_managed' \ 77 | -H 'accept: */*' \ 78 | -H 'Authorization: Bearer ${{ secrets.MAVEN_PORTAL_TOKEN }}' \ 79 | -d '' 80 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea 8 | .idea/modules.xml 9 | .idea/jarRepositories.xml 10 | .idea/compiler.xml 11 | .idea/libraries/ 12 | *.iws 13 | *.iml 14 | *.ipr 15 | 16 | ### Eclipse ### 17 | .apt_generated 18 | .classpath 19 | .factorypath 20 | .project 21 | .settings 22 | .springBeans 23 | .sts4-cache 24 | 25 | ### NetBeans ### 26 | /nbproject/private/ 27 | /nbbuild/ 28 | /dist/ 29 | /nbdist/ 30 | /.nb-gradle/ 31 | build/ 32 | !**/src/main/**/build/ 33 | !**/src/test/**/build/ 34 | 35 | ### VS Code ### 36 | .vscode/ 37 | 38 | ### Mac OS ### 39 | .DS_Store 40 | 41 | ### Gradle ### 42 | .gradle -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our project a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behaviour that contributes to creating a positive environment include: 10 | 11 | - Using welcoming and inclusive language 12 | - Being respectful of differing viewpoints and experiences 13 | - Gracefully accepting constructive criticism 14 | - Focusing on what is best for the community 15 | - Showing empathy towards other community members 16 | 17 | Examples of unacceptable behaviour by participants include: 18 | 19 | - The use of sexualised language or imagery, and unwelcome sexual attention or advances 20 | - Trolling, insulting, or derogatory comments 21 | - Personal or political attacks 22 | - Public or private harassment 23 | - Publishing others’ private information, such as a physical or electronic address, without explicit permission 24 | - Other conduct which could reasonably be considered inappropriate in a professional setting 25 | 26 | ## Enforcement Responsibilities 27 | 28 | Project maintainers are responsible for clarifying the standards of acceptable behaviour and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behaviour. 29 | 30 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that do not align with this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. 31 | 32 | ## Scope 33 | 34 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project email address, posting via an official social media account, or acting as an appointed representative at an online or offline event. 35 | 36 | ## Enforcement 37 | 38 | Instances of abusive, harassing, or unacceptable behaviour may be reported by contacting the project team at [your-email@example.com]. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. 39 | 40 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 41 | 42 | ## Attribution 43 | 44 | This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), version 1.4, available at https://www.contributor-covenant.org/version/1/4/ 45 | 46 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Java Dev Kit 2 | 3 | We appreciate your interest in contributing to our Java Enterprise Utility Library. Contributions are welcome in various 4 | forms such as code, documentation, bug reports, and test cases. To ensure a smooth collaboration, please follow the 5 | guidelines outlined below. 6 | 7 | ## Getting Started 8 | 9 | The Java Dev Kit is an open-source Java library designed to ease Java enterprise application development. It is built 10 | with Java 17, and we require contributors to use OpenJDK 17. 11 | 12 | ## Development Setup 13 | 14 | There is no need for manual setup beyond ensuring you have JDK 17 and a stable internet connection. Our project uses 15 | Gradle Wrapper for building, simplifying the setup process. 16 | 17 | ```shell 18 | ./gradlew build 19 | ``` 20 | 21 | ## Branching Strategy 22 | 23 | We follow the `git-flow` branching model. Contributors should fork this repository and create their work on separate 24 | branches prefixed with `feature/` or `hotfix/` as appropriate. 25 | 26 | ## Code Style 27 | 28 | Please adhere to the coding standards specified in our `.editorconfig` file in the root of the repository. Consistent 29 | style helps in maintaining readability and uniformity across the codebase. 30 | 31 | ## Commit Messages 32 | 33 | We require that commit messages follow this structure: 34 | 35 | ```text 36 | type[(scope)]: subject 37 | 38 | [body] 39 | 40 | [BREAKING CHANGE: breaking changes] 41 | ``` 42 | 43 | This format helps in auto-generating changelogs and understanding the purpose behind changes. 44 | 45 | ## Submitting Contributions 46 | 47 | 1. Fork the repository. 48 | 2. Create a branch: `git checkout -b feature/my-new-feature` or `git checkout -b hotfix/my-fix`. 49 | 3. Make your changes and commit them. 50 | 4. Push to the branch: `git push origin feature/my-new-feature`. 51 | 5. Create a Pull Request with a succinct subject and a detailed body. 52 | 53 | ## Testing 54 | 55 | We encourage using JUnit Jupiter for unit and integration tests. Pull Requests with accompanying test reports are prioritised for review and merging. 56 | 57 | ## License 58 | 59 | By contributing to the Java Dev Kit, you agree that your contributions will be licensed under the Apache 2.0 license. If you do not agree to this, please refrain from contributing. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JDevKit 2 | 3 | ![Static Badge](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Frepo1.maven.org%2Fmaven2%2Fcom%2Fonixbyte%2Fdevkit-bom%2Fmaven-metadata.xml&query=%2F%2Fmetadata%2Fversioning%2Flatest&label=version) 4 | ![Static Badge](https://img.shields.io/badge/licence-Apache_2.0-green) 5 | ![Static Badge](https://img.shields.io/badge/JDK-%E2%89%A517-blue) 6 | 7 | 8 | JDevKit is a Java Development Kit that offers a set of convenient tools for writing code efficiently. 9 | 10 | ## Installation and Usage 11 | 12 | If you are using **Maven**, please paste the following codes to _pom.xml_ in your project. 13 | 14 | ```xml 15 | 16 | com.onixbyte 17 | ${artifactId} 18 | ${version} 19 | 20 | ``` 21 | 22 | If you are using **Gradle**, please paste the following codes to _buile.gradle\[.kts\]_ in your project. 23 | 24 | ```groovy 25 | implementation 'com.onixbyte:$artifactId:$version' 26 | ``` 27 | 28 | ```kotlin 29 | implementation("com.onixbyte:$artifactId:$version") 30 | ``` 31 | 32 | If you want to check the available versions, please check out at our [official site](https://codecrafters.org.cn/devkit/changelog). 33 | 34 | ## Contribution 35 | Contributions are welcome! If you encounter any issues or want to contribute to the project, please feel free to **[raise an issue](https://github.com/CodeCraftersCN/jdevkit/issues/new)** or **[submit a pull request](https://github.com/CodeCraftersCN/jdevkit/compare)**. 36 | 37 | ## License 38 | This project is licensed under the [Apache License 2.0](LICENSE). 39 | 40 | ## Contact 41 | 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). 42 | 43 | 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). -------------------------------------------------------------------------------- /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 | */ -------------------------------------------------------------------------------- /devkit-bom/README.md: -------------------------------------------------------------------------------- 1 | # DevKit BOM 2 | 3 | The devkit-bom (Bill of Materials) is a Maven POM file provided by OnixByte to manage dependency versions for the DevKit suite of libraries. By incorporating this BOM into your build configuration, you can ensure consistent versioning across all included dependencies without needing to specify versions explicitly in your project files. Published with Gradle metadata, this BOM supports both Maven and Gradle projects, and this document outlines how to integrate and use it effectively in both ecosystems. 4 | 5 | ## Using in Maven 6 | 7 | Add the `devkit-bom` to your `pom.xml` under ``: 8 | 9 | ```xml 10 | 11 | 12 | 13 | com.onixbyte 14 | devkit-bom 15 | ${devkit-version} 16 | pom 17 | import 18 | 19 | 20 | 21 | ``` 22 | 23 | Then reference dependencies like `devkit-core` without a version. 24 | 25 | ## Using in Gradle 26 | 27 | In your `build.gradle[.kts]`, apply the BOM using the `platform` dependency: 28 | 29 | ```groovy 30 | dependencies { 31 | implementation platform('com.onixbyte:devkit-bom:2.0.0') 32 | implementation 'com.onixbyte:devkit-core' 33 | implementation 'com.onixbyte:devkit-utils' 34 | } 35 | ``` 36 | 37 | If you are using Kotlin DSL: 38 | 39 | ```kotlin 40 | dependencies { 41 | implementation(platform("com.onixbyte:devkit-bom:2.0.0")) 42 | implementation("com.onixbyte:devkit-core") 43 | implementation("com.onixbyte:devkit-utils") 44 | } 45 | ``` 46 | 47 | -------------------------------------------------------------------------------- /devkit-bom/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 | id("java-platform") 21 | id("maven-publish") 22 | id("signing") 23 | } 24 | 25 | val artefactVersion: String by project 26 | val projectUrl: String by project 27 | val projectGithubUrl: String by project 28 | val licenseName: String by project 29 | val licenseUrl: String by project 30 | 31 | group = "com.onixbyte" 32 | version = artefactVersion 33 | 34 | repositories { 35 | mavenCentral() 36 | } 37 | 38 | dependencies { 39 | constraints { 40 | api("com.onixbyte:devkit-core:$artefactVersion") 41 | api("com.onixbyte:devkit-utils:$artefactVersion") 42 | api("com.onixbyte:guid:$artefactVersion") 43 | api("com.onixbyte:key-pair-loader:$artefactVersion") 44 | api("com.onixbyte:map-util-unsafe:$artefactVersion") 45 | api("com.onixbyte:num4j:$artefactVersion") 46 | api("com.onixbyte:simple-jwt-facade:$artefactVersion") 47 | api("com.onixbyte:simple-jwt-authzero:$artefactVersion") 48 | api("com.onixbyte:simple-jwt-spring-boot-starter:$artefactVersion") 49 | api("com.onixbyte:property-guard-spring-boot-starter:$artefactVersion") 50 | api("com.onixbyte:simple-serial-spring-boot-starter:$artefactVersion") 51 | } 52 | } 53 | 54 | publishing { 55 | publications { 56 | create("devkitBom") { 57 | groupId = group.toString() 58 | artifactId = "devkit-bom" 59 | version = artefactVersion 60 | 61 | pom { 62 | name = "DevKit BOM" 63 | description = "Using BOM could use unified OnixByte JDevKit." 64 | url = projectUrl 65 | 66 | licenses { 67 | license { 68 | name = licenseName 69 | url = licenseUrl 70 | } 71 | } 72 | 73 | scm { 74 | connection = "scm:git:git://github.com:OnixByte/devkit-bom.git" 75 | developerConnection = "scm:git:git://github.com:OnixByte/devkit-bom.git" 76 | url = projectGithubUrl 77 | } 78 | 79 | developers { 80 | developer { 81 | id = "zihluwang" 82 | name = "Zihlu Wang" 83 | email = "really@zihlu.wang" 84 | timezone = "Asia/Hong_Kong" 85 | } 86 | 87 | developer { 88 | id = "siujamo" 89 | name = "Siu Jam'o" 90 | email = "jamo.siu@outlook.com" 91 | timezone = "Asia/Shanghai" 92 | } 93 | } 94 | } 95 | 96 | print(components) 97 | 98 | from(components["javaPlatform"]) 99 | 100 | signing { 101 | sign(publishing.publications["devkitBom"]) 102 | } 103 | } 104 | 105 | repositories { 106 | maven { 107 | name = "sonatypeNexus" 108 | url = URI(providers.gradleProperty("repo.maven-central.host").get()) 109 | credentials { 110 | username = providers.gradleProperty("repo.maven-central.username").get() 111 | password = providers.gradleProperty("repo.maven-central.password").get() 112 | } 113 | } 114 | } 115 | } 116 | } -------------------------------------------------------------------------------- /devkit-core/README.md: -------------------------------------------------------------------------------- 1 | # Module `devkit-core` 2 | 3 | ## Introduction 4 | 5 | This module serves as the basement of `JDevKit`. It provides some base exceptions that `JDevKit` might use. -------------------------------------------------------------------------------- /devkit-core/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 | 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("devkitCore") { 69 | groupId = group.toString() 70 | artifactId = "devkit-core" 71 | version = artefactVersion 72 | 73 | pom { 74 | name = "DevKit - Core" 75 | description = "The core 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["devkitCore"]) 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-core/src/main/java/com/onixbyte/devkit/core/exceptions/NotImplementedException.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.core.exceptions; 19 | 20 | /** 21 | * The {@code NotImplementedException} class is a custom runtime exception that represents a 22 | * situation where a particular method or functionality is not implemented or is currently 23 | * unavailable in the codebase. 24 | *

25 | * This exception is typically thrown when developers need to indicate that a specific part of the 26 | * code is incomplete or requires further implementation. It serves as a placeholder to highlight 27 | * unfinished sections of the application during development and testing phases. 28 | *

29 | * Usage Example: 30 | *

31 |  * public void someMethod() {
32 |  *     // Some code...
33 |  *     throw new NotImplementedException("""
34 |  *     This feature will be implemented in a future release.""");
35 |  * }
36 |  * 
37 | * 38 | * @author Zihlu Wang 39 | * @version 1.1.0 40 | * @see RuntimeException 41 | * @since 1.0.0 42 | */ 43 | public class NotImplementedException extends RuntimeException { 44 | 45 | /** 46 | * Creates a new {@code NotImplementedException} with no specific error message. 47 | */ 48 | public NotImplementedException() { 49 | } 50 | 51 | /** 52 | * Creates a new {@code NotImplementedException} with the provided error message. 53 | * 54 | * @param message the error message associated with this exception 55 | */ 56 | public NotImplementedException(String message) { 57 | super(message); 58 | } 59 | 60 | /** 61 | * Creates a new {@code NotImplementedException} with the specified error message and a cause 62 | * for this exception. 63 | * 64 | * @param message the error message associated with this exception 65 | * @param cause the cause of this exception 66 | */ 67 | public NotImplementedException(String message, Throwable cause) { 68 | super(message, cause); 69 | } 70 | 71 | /** 72 | * Creates a new {@code NotImplementedException} with the specified cause. 73 | * 74 | * @param cause the cause of this exception 75 | */ 76 | public NotImplementedException(Throwable cause) { 77 | super(cause); 78 | } 79 | 80 | /** 81 | * Creates a new {@code NotImplementedException} with the specified error message, cause, 82 | * suppression flag, and stack trace writable flag. 83 | * 84 | * @param message the error message associated with this 85 | * exception 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 NotImplementedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 91 | super(message, cause, enableSuppression, writableStackTrace); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /devkit-core/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-core/src/test/java/com/onixbyte/devkit/core/exceptions/NotImplementationExceptionTest.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.core.exceptions; 19 | 20 | import org.junit.jupiter.api.Test; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | 24 | public class NotImplementationExceptionTest { 25 | 26 | private final static Logger log = LoggerFactory.getLogger(NotImplementationExceptionTest.class); 27 | 28 | @Test 29 | public void testExceptionWithEmptyConstructor() { 30 | try { 31 | throw new NotImplementedException(); 32 | } catch (NotImplementedException e) { 33 | log.error("NotImplementedException: ", e); 34 | } 35 | } 36 | 37 | @Test 38 | public void testExceptionWithStringConstructor() { 39 | try { 40 | throw new NotImplementedException("This function is not implemented yet, please contact developer for further information."); 41 | } catch (NotImplementedException e) { 42 | log.error("NotImplementedException: ", e); 43 | } 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /devkit-utils/README.md: -------------------------------------------------------------------------------- 1 | # Module `devkit-utils` 2 | 3 | ## Introduction 4 | 5 | This module provides a set of utilities to streamline Java codes. 6 | 7 | ## Features 8 | 9 | - AES encryption and decryption; 10 | - Base64 encode and decode; 11 | - Boolean calculation; 12 | - Reduce `if...else...` with **lambdas**; 13 | - Hash calculation for strings; 14 | - Convert Java beans to map and map to Java beans; 15 | - Simplified range generator. 16 | 17 | -------------------------------------------------------------------------------- /devkit-utils/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("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 | --------------------------------------------------------------------------------