diff --git a/settings.gradle.kts b/settings.gradle.kts index 1a7eb27..5ad86ae 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -29,3 +29,4 @@ include( "simple-jwt-spring-boot-starter", "property-guard-spring-boot-starter" ) +include("simple-serial") diff --git a/simple-serial/README.md b/simple-serial/README.md new file mode 100644 index 0000000..8a4d20c --- /dev/null +++ b/simple-serial/README.md @@ -0,0 +1,14 @@ +# Simple Serial + +> Thanks to [@siujamo](https://github.com/siujamo)'s donation. + +Simple Serial reuses the configuration of Redis connections to provide am easy-to-use serial +service. + +## Configuration + +Simple Serial reused the redis configuration of Spring Boot to provide redis support for the +service. + +Besides, **Simple Serial** provides a configuration property `onixbyte.serial.start-serial` to +specify the start value of a serial, and default to `0`. \ No newline at end of file diff --git a/simple-serial/build.gradle.kts b/simple-serial/build.gradle.kts new file mode 100644 index 0000000..36f1eda --- /dev/null +++ b/simple-serial/build.gradle.kts @@ -0,0 +1,99 @@ +import java.net.URI + +plugins { + id("java") +} + +val artefactVersion: String by project +val projectUrl: String by project +val projectGithubUrl: String by project +val licenseName: String by project +val licenseUrl: String by project + +val jacksonVersion: String by project +val springBootVersion: String by project + +group = "com.onixbyte" +version = artefactVersion + +repositories { + mavenCentral() +} + +dependencies { + implementation("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion") + implementation("org.springframework.boot:spring-boot-autoconfigure:$springBootVersion") + implementation("org.springframework.boot:spring-boot-starter-logging:$springBootVersion") + implementation("org.springframework.boot:spring-boot-configuration-processor:$springBootVersion") + implementation("org.springframework.boot:spring-boot-starter-data-redis:$springBootVersion") + annotationProcessor("org.springframework.boot:spring-boot-configuration-processor:$springBootVersion") + testImplementation("org.springframework.boot:spring-boot-starter-test:$springBootVersion") + testImplementation(platform("org.junit:junit-bom:5.10.0")) + testImplementation("org.junit.jupiter:junit-jupiter") +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + withSourcesJar() + withJavadocJar() +} + +tasks.test { + useJUnitPlatform() +} + +publishing { + publications { + create("simpleSerialSpringBootStarter") { + groupId = group.toString() + artifactId = "simple-serial-spring-boot-starter" + version = artefactVersion + + pom { + name = "Simple Serial :: Spring Boot Starter" + description = "A Redis based easy-to-use serial service." + url = projectUrl + + licenses { + license { + name = licenseName + url = licenseUrl + } + } + + scm { + connection = "scm:git:git://github.com:OnixByte/JDevKit.git" + developerConnection = "scm:git:git://github.com:OnixByte/JDevKit.git" + url = projectGithubUrl + } + + developers { + developer { + id = "zihluwang" + name = "Zihlu Wang" + email = "really@zihlu.wang" + timezone = "Asia/Hong_Kong" + } + } + } + + from(components["java"]) + + signing { + sign(publishing.publications["simpleSerialSpringBootStarter"]) + } + } + + repositories { + maven { + name = "sonatypeNexus" + url = URI(providers.gradleProperty("repo.maven-central.host").get()) + credentials { + username = providers.gradleProperty("repo.maven-central.username").get() + password = providers.gradleProperty("repo.maven-central.password").get() + } + } + } + } +} \ No newline at end of file diff --git a/simple-serial/src/main/java/com/onixbyte/serial/RedisConfig.java b/simple-serial/src/main/java/com/onixbyte/serial/RedisConfig.java new file mode 100644 index 0000000..99a4527 --- /dev/null +++ b/simple-serial/src/main/java/com/onixbyte/serial/RedisConfig.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2024-2025 OnixByte. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.onixbyte.serial; + +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.RedisSerializer; + +/** + * Redis Configuration provides redis templates for operations to serial. + * + * @author siujamo + */ +@AutoConfiguration +public class RedisConfig { + + /** + * RedisTemplate for serial service. + * + * @param redisConnectionFactory redis connection factory + * @return a configured redis template for serial service + */ + @Bean + public RedisTemplate serialRedisTemplate(RedisConnectionFactory redisConnectionFactory) { + var redisTemplate = new RedisTemplate(); + redisTemplate.setConnectionFactory(redisConnectionFactory); + redisTemplate.setKeySerializer(RedisSerializer.string()); + redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(Long.class)); + redisTemplate.afterPropertiesSet(); + return redisTemplate; + } + +} diff --git a/simple-serial/src/main/java/com/onixbyte/serial/SerialService.java b/simple-serial/src/main/java/com/onixbyte/serial/SerialService.java new file mode 100644 index 0000000..55d6fbf --- /dev/null +++ b/simple-serial/src/main/java/com/onixbyte/serial/SerialService.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2024-2025 OnixByte. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.onixbyte.serial; + +import com.onixbyte.serial.properties.SerialProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; + +import java.util.Optional; + +/** + * {@code SerialService} provides simple serial operations. + * + * @author siujamo + */ +@Service +@EnableConfigurationProperties(SerialProperties.class) +public class SerialService { + + private static final Logger log = LoggerFactory.getLogger(SerialService.class); + + private final String appName; + private final RedisTemplate serialRedisTemplate; + private final SerialProperties serialProperties; + + /** + * Default constructor. + * + * @param appName the name of this application + * @param serialRedisTemplate serial redis template + * @param serialProperties serial properties + */ + public SerialService(@Value("${spring.application.name}") String appName, + RedisTemplate serialRedisTemplate, + SerialProperties serialProperties) { + this.appName = appName; + this.serialRedisTemplate = serialRedisTemplate; + this.serialProperties = serialProperties; + } + + /** + * Build a serial key. + * + * @param tag tag of the serial + * @return key of a serial + */ + public String buildKey(String tag) { + return appName + ":serial:" + tag; + } + + /** + * Get the next available serial for specific tag. + * + * @param tag tag of the serial + * @return next available serial + */ + public Long nextSerial(String tag) { + var key = buildKey(tag); + var next = Optional.ofNullable(serialRedisTemplate.opsForValue().get(key)) + .orElse(serialProperties.getStartSerial()); + serialRedisTemplate.opsForValue().set(key, next + 1); + return next; + } + + /** + * Reset all serial values. + */ + public void reset() { + var keys = serialRedisTemplate.keys(buildKey("*")); + var startSerial = serialProperties.getStartSerial(); + + if (!keys.isEmpty()) { + for (var key : keys) { + serialRedisTemplate.opsForValue().set(key, startSerial); + log.debug("Serial {} has been reset to {}", key, startSerial); + } + } + } + +} diff --git a/simple-serial/src/main/java/com/onixbyte/serial/properties/SerialProperties.java b/simple-serial/src/main/java/com/onixbyte/serial/properties/SerialProperties.java new file mode 100644 index 0000000..cb2552d --- /dev/null +++ b/simple-serial/src/main/java/com/onixbyte/serial/properties/SerialProperties.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2024-2025 OnixByte. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.onixbyte.serial.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * SerialProperties can modify the start value of a serial. + * + * @author siujamo + */ +@ConfigurationProperties(prefix = "onixbyte.serial") +public class SerialProperties { + + /** + * The start of the serial, default to 0. + */ + private Long startSerial = 0L; + + /** + * Get the start of the serial. + * + * @return start of the serial + */ + public Long getStartSerial() { + return startSerial; + } + + /** + * Set the start of the serial. + * + * @param startSerial start of the serial + */ + public void setStartSerial(Long startSerial) { + this.startSerial = startSerial; + } + + /** + * Default constructor. + */ + public SerialProperties() { + } + +} diff --git a/simple-serial/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/simple-serial/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..7fd0a37 --- /dev/null +++ b/simple-serial/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,19 @@ +# +# Copyright (C) 2024-2025 OnixByte. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# +# See the License for the specific language governing permissions and +# limitations under the License. +# + +com.onixbyte.serial.RedisConfig +com.onixbyte.serial.SerialService \ No newline at end of file diff --git a/simple-serial/src/main/resources/logback.xml b/simple-serial/src/main/resources/logback.xml new file mode 100644 index 0000000..fd31eac --- /dev/null +++ b/simple-serial/src/main/resources/logback.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + ${COLOURFUL_OUTPUT} + + + + + + \ No newline at end of file