+ * This annotation is typically applied to methods in a service layer where
+ * performance monitoring is essential. The annotation can be configured to log
+ * specific types of data, such as input arguments, output results, and the
+ * duration of method execution. The logged information can then be used for
+ * performance analysis, debugging, and auditing purposes.
+ *
+ * In this example, the `fetchData` method is annotated with
+ * {@code @LogPerformance}, which will log the input arguments, output results,
+ * and execution duration when the method is called. The log will include the
+ * specified `logType`, `actionType`, and `title`.
+ *
+ *
+ *
Retention and Target:
+ *
+ * The annotation is retained at runtime ({@link RetentionPolicy#RUNTIME}) and
+ * can be applied to both methods and types (classes or interfaces)
+ * ({@link ElementType#METHOD}, {@link ElementType#TYPE}).
+ *
+ */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface LogPerformance {
+
+ /**
+ * Specifies the type of log entry, such as "API_CALL", "DB_QUERY", etc.
+ *
+ * This value helps categorize logs based on the type of operation being logged.
+ * For instance, you might use different log types for API calls versus database
+ * queries to differentiate them in your log analysis.
+ *
+ *
+ * @return the type of log entry
+ */
String logType() default "";
+ /**
+ * Specifies the type of action being performed, such as "CREATE", "UPDATE",
+ * "DELETE", etc.
+ *
+ * This value can be used to identify what kind of action is being logged,
+ * providing more context for the log entry. For example, you might have actions
+ * like "FETCH_DATA", "SAVE_ENTITY", etc.
+ *
+ *
+ * @return the type of action being logged
+ */
String actionType() default "";
+ /**
+ * Indicates whether the output (result) of the method should be logged.
+ *
+ * When set to {@code true}, the output of the method will be logged, provided
+ * it can be serialized. This is useful for tracing the results of method
+ * executions, especially when debugging or monitoring system behavior.
+ *
+ *
+ * @return {@code true} if the output should be logged, {@code false} otherwise
+ */
boolean logOutput() default true;
+ /**
+ * Indicates whether the input arguments of the method should be logged.
+ *
+ * When set to {@code true}, the input arguments to the method will be logged.
+ * This is useful for capturing the state of the inputs when the method was
+ * called, which can be important for understanding the context of the log
+ * entry.
+ *
+ *
+ * @return {@code true} if the input arguments should be logged, {@code false}
+ * otherwise
+ */
boolean logInput() default true;
+ /**
+ * Specifies a custom title for the log entry.
+ *
+ * This title can be used to provide a brief description of the logged
+ * operation, making it easier to identify and search for specific log entries
+ * in your logging system. For example, the title might be something like "User
+ * Login Attempt" or "Order Processing".
+ *
+ *
+ * @return the custom title for the log entry
+ */
String title() default "";
}
diff --git a/src/main/java/io/hoangtien2k3/commons/annotations/cache/CustomizeRemovalListener.java b/src/main/java/io/hoangtien2k3/commons/annotations/cache/CustomizeRemovalListener.java
index b7bdb4a..ec53201 100644
--- a/src/main/java/io/hoangtien2k3/commons/annotations/cache/CustomizeRemovalListener.java
+++ b/src/main/java/io/hoangtien2k3/commons/annotations/cache/CustomizeRemovalListener.java
@@ -22,16 +22,62 @@
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
+/**
+ * This class implements the {@link RemovalListener} interface and provides
+ * custom handling for cache entry removals. It logs information when a cache
+ * entry is evicted and invokes a specified method.
+ *
+ *
+ * The class uses Lombok annotations {@code @Slf4j} for logging and
+ * {@code @AllArgsConstructor} to generate a constructor with one parameter for
+ * each field in the class.
+ *
+ *
+ * This listener is typically used in caching mechanisms where specific actions
+ * need to be taken when an entry is removed due to eviction.
+ *
+ *
+ * The listener relies on a {@link Method} instance, which is passed through the
+ * constructor. This method is invoked whenever a cache entry associated with it
+ * is evicted.
+ *
+ *
+ *
+ * @author Your Name
+ */
@Slf4j
@AllArgsConstructor
public class CustomizeRemovalListener implements RemovalListener {
- private Method method;
+ private final Method method;
+ /**
+ * Handles the removal of a cache entry. If the removal cause is eviction, this
+ * method logs the event and invokes the method provided during the creation of
+ * this listener.
+ *
+ * @param key
+ * the key of the removed cache entry, can be null
+ * @param value
+ * the value of the removed cache entry, can be null
+ * @param removalCause
+ * the cause of the removal, never null
+ */
@Override
public void onRemoval(@Nullable Object key, @Nullable Object value, @NonNull RemovalCause removalCause) {
if (removalCause.wasEvicted()) {
- log.info("Cache " + method.getDeclaringClass().getSimpleName() + "." + method.getName()
- + " was evicted because " + removalCause);
+ log.info(
+ "Cache {}.{} was evicted because {}",
+ method.getDeclaringClass().getSimpleName(),
+ method.getName(),
+ removalCause);
CacheUtils.invokeMethod(method);
}
}
diff --git a/src/main/java/io/hoangtien2k3/commons/annotations/logging/LoggerAspect.java b/src/main/java/io/hoangtien2k3/commons/annotations/logging/LoggerAspect.java
index a8f0400..c7bd70e 100644
--- a/src/main/java/io/hoangtien2k3/commons/annotations/logging/LoggerAspect.java
+++ b/src/main/java/io/hoangtien2k3/commons/annotations/logging/LoggerAspect.java
@@ -21,12 +21,59 @@
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.Configuration;
+/**
+ * This aspect class is responsible for logging the performance of methods in
+ * the specified packages. It uses AOP (Aspect-Oriented Programming) to
+ * intercept method calls and log relevant information before and after the
+ * method execution.
+ *
+ *
+ * The class uses Spring AOP and relies on two pointcuts to identify which
+ * methods should be logged:
+ *
+ *
{@code performancePointCut}: Matches methods in the specified packages
+ * (controller, service, repository, client) except for those in the Spring Boot
+ * Actuator package.
+ *
{@code logPerfMethods}: Matches methods annotated with
+ * {@link io.hoangtien2k3.commons.annotations.LogPerformance}.
+ *
+ *
+ *
+ * The {@code @Around} advice is applied to the methods matched by these
+ * pointcuts, and it delegates the logging logic to {@link LoggerAspectUtils}.
+ *
+ *
+ * The class is annotated with {@code @Aspect} to define it as an aspect, and
+ * {@code @Configuration} to ensure it is registered as a Spring bean. It also
+ * uses {@code @RequiredArgsConstructor} from Lombok to generate a constructor
+ * that injects the required dependencies.
+ *
+ * @author hoangtien2k3
+ */
@Aspect
@Configuration
@RequiredArgsConstructor
public class LoggerAspect {
+
private final LoggerAspectUtils loggerAspectUtils;
+ /**
+ * Pointcut that matches the execution of any method within the specified
+ * packages (controller, service, repository, client) except for those under the
+ * Spring Boot Actuator package.
+ */
@Pointcut("(execution(* io.hoangtien2k3.commons.*.controller..*(..)) || "
+ "execution(* io.hoangtien2k3.commons.*.service..*(..)) || "
+ "execution(* io.hoangtien2k3.commons.*.repository..*(..)) || "
@@ -34,9 +81,24 @@ public class LoggerAspect {
+ "!execution(* org.springframework.boot.actuate..*(..))")
public void performancePointCut() {}
+ /**
+ * Pointcut that matches methods annotated with
+ * {@link io.hoangtien2k3.commons.annotations.LogPerformance}.
+ */
@Pointcut("@annotation(io.hoangtien2k3.commons.annotations.LogPerformance)")
private void logPerfMethods() {}
+ /**
+ * Around advice that wraps the matched method executions, providing logging
+ * functionality around the method invocation. The actual logging logic is
+ * delegated to {@link LoggerAspectUtils#logAround}.
+ *
+ * @param joinPoint
+ * the join point representing the method execution
+ * @return the result of the method execution
+ * @throws Throwable
+ * if the method execution throws an exception
+ */
@Around("performancePointCut() || logPerfMethods()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
return loggerAspectUtils.logAround(joinPoint);
diff --git a/src/main/java/io/hoangtien2k3/commons/annotations/logging/LoggerAspectUtils.java b/src/main/java/io/hoangtien2k3/commons/annotations/logging/LoggerAspectUtils.java
index cbf6efd..a7c6f8e 100644
--- a/src/main/java/io/hoangtien2k3/commons/annotations/logging/LoggerAspectUtils.java
+++ b/src/main/java/io/hoangtien2k3/commons/annotations/logging/LoggerAspectUtils.java
@@ -33,6 +33,41 @@
import reactor.core.publisher.Mono;
import reactor.util.context.Context;
+/**
+ * Utility class for logging method execution performance using Aspect-Oriented
+ * Programming (AOP) in Spring. This class handles logging for methods returning
+ * reactive types (`Mono` and `Flux`) and integrates with distributed tracing
+ * using Spring Cloud Sleuth.
+ *
+ *
+ * The class provides functionality to:
+ *
+ *
Log method execution time for methods matched by the logging aspect.
+ *
Handle logging of input and output data conditionally based on the
+ * {@link LogPerformance} annotation.
+ *
Capture and log exceptions, with optional detailed logging based on
+ * configuration.
+ *
+ *
+ *
+ * This class is annotated with `@Component` to be managed by the Spring
+ * container and `@RequiredArgsConstructor` to inject required dependencies
+ * automatically.
+ *
+ *
+ *
+ * @author Your Name
+ */
@Component
@RequiredArgsConstructor
public class LoggerAspectUtils {
@@ -45,9 +80,28 @@ public class LoggerAspectUtils {
@Value("${debug.detailException:true}")
private boolean detailException;
+ /**
+ * Initializes the utility class after the dependency injection is done to
+ * perform any necessary setup.
+ */
@PostConstruct
- private void init() {}
+ private void init() {
+ // Initialization logic if necessary
+ }
+ /**
+ * Logs method execution performance around the join point. It handles logging
+ * for methods that return reactive types (`Mono` or `Flux`). The logging
+ * includes input arguments, output results, execution time, and any exceptions
+ * that occur during method execution.
+ *
+ * @param joinPoint
+ * the join point representing the method execution
+ * @return the result of the method execution, possibly wrapped in a `Mono` or
+ * `Flux`
+ * @throws Throwable
+ * if the method execution throws an exception
+ */
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
@@ -79,20 +133,50 @@ public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
var result = joinPoint.proceed();
if (result instanceof Mono) {
return logMonoResult(
- joinPoint, start, (Mono) result, newSpan, name, logType, actionType, logOutput, logInput, title);
+ joinPoint, start, (Mono>) result, newSpan, name, logType, actionType, logOutput, logInput, title);
}
if (result instanceof Flux) {
return logFluxResult(
- joinPoint, start, (Flux) result, newSpan, name, logType, actionType, logOutput, logInput, title);
+ joinPoint, start, (Flux>) result, newSpan, name, logType, actionType, logOutput, logInput, title);
} else {
return result;
}
}
- private Mono logMonoResult(
+ /**
+ * Handles logging for methods that return a `Mono`. It logs the input
+ * arguments, output results, execution time, and any exceptions that occur
+ * during the execution of the `Mono`.
+ *
+ * @param joinPoint
+ * the join point representing the method execution
+ * @param start
+ * the start time of the method execution
+ * @param result
+ * the `Mono` returned by the method
+ * @param newSpan
+ * the tracing span associated with the method execution
+ * @param name
+ * the name of the method being logged
+ * @param logType
+ * the type of log (can be customized via `LogPerformance`
+ * annotation)
+ * @param actionType
+ * the action type (can be customized via `LogPerformance`
+ * annotation)
+ * @param logOutput
+ * whether to log the output of the method
+ * @param logInput
+ * whether to log the input arguments of the method
+ * @param title
+ * the title for the log entry (can be customized via
+ * `LogPerformance` annotation)
+ * @return the `Mono` with added logging
+ */
+ private Mono> logMonoResult(
ProceedingJoinPoint joinPoint,
long start,
- Mono result,
+ Mono> result,
Span newSpan,
String name,
String logType,
@@ -115,10 +199,6 @@ private Mono logMonoResult(
.contextWrite(context -> {
var currContext = (Context) context;
contextRef.set(currContext);
- // the error happens in a different thread, so get the trace from context, set
- // in MDC
- // and downstream
- // to doOnError
return context;
})
.doOnError(o -> {
@@ -133,10 +213,40 @@ private Mono logMonoResult(
});
}
- private Flux logFluxResult(
+ /**
+ * Handles logging for methods that return a `Flux`. It logs the input
+ * arguments, output results, execution time, and any exceptions that occur
+ * during the execution of the `Flux`.
+ *
+ * @param joinPoint
+ * the join point representing the method execution
+ * @param start
+ * the start time of the method execution
+ * @param result
+ * the `Flux` returned by the method
+ * @param newSpan
+ * the tracing span associated with the method execution
+ * @param name
+ * the name of the method being logged
+ * @param logType
+ * the type of log (can be customized via `LogPerformance`
+ * annotation)
+ * @param actionType
+ * the action type (can be customized via `LogPerformance`
+ * annotation)
+ * @param logOutput
+ * whether to log the output of the method
+ * @param logInput
+ * whether to log the input arguments of the method
+ * @param title
+ * the title for the log entry (can be customized via
+ * `LogPerformance` annotation)
+ * @return the `Flux` with added logging
+ */
+ private Flux> logFluxResult(
ProceedingJoinPoint joinPoint,
long start,
- Flux result,
+ Flux> result,
Span newSpan,
String name,
String logType,
@@ -151,10 +261,6 @@ private Flux logFluxResult(
.contextWrite(context -> {
var currContext = (Context) context;
contextRef.set(currContext);
- // the error happens in a different thread, so get the trace from context, set
- // in MDC
- // and downstream
- // to doOnError
return context;
})
.doOnError(o -> {
@@ -162,6 +268,23 @@ private Flux logFluxResult(
});
}
+ /**
+ * Logs performance details such as method name, duration, result, and any
+ * exceptions encountered during execution.
+ *
+ * @param contextRef
+ * reference to the current context
+ * @param newSpan
+ * the tracing span associated with the method execution
+ * @param name
+ * the name of the method being logged
+ * @param start
+ * the start time of the method execution
+ * @param result
+ * the result code ("0" for success, "1" for failure)
+ * @param o
+ * the output object or exception
+ */
private void logPerf(
AtomicReference contextRef, Span newSpan, String name, Long start, String result, Object o) {
newSpan.finish();
@@ -170,6 +293,21 @@ private void logPerf(
logPerf.info("{} {} {} M2 {}", name, duration, result, o == null ? "-" : o.toString());
}
+ /**
+ * Logs performance details with additional parameters for more granular
+ * control.
+ *
+ * @param contextRef
+ * reference to the current context
+ * @param newSpan
+ * the tracing span associated with the method execution
+ * @param name
+ * the name of the method being logged
+ * @param startTime
+ * the start time of the method execution
+ * @param result
+ * the result code
+ */
private void logPerf(
AtomicReference contextRef,
Span newSpan,
diff --git a/src/main/java/io/hoangtien2k3/commons/annotations/logging/LoggerQueue.java b/src/main/java/io/hoangtien2k3/commons/annotations/logging/LoggerQueue.java
index c58e0a7..a4648b3 100644
--- a/src/main/java/io/hoangtien2k3/commons/annotations/logging/LoggerQueue.java
+++ b/src/main/java/io/hoangtien2k3/commons/annotations/logging/LoggerQueue.java
@@ -24,18 +24,38 @@
import lombok.extern.slf4j.Slf4j;
import reactor.util.context.Context;
+/**
+ * LoggerQueue is a singleton class that manages a bounded blocking queue for
+ * logging operations. It is designed to handle high-throughput logging tasks by
+ * storing logs in a queue and providing various methods to interact with the
+ * queue.
+ */
@Slf4j
public class LoggerQueue {
+
+ /** Singleton instance of LoggerQueue */
private static LoggerQueue mMe = null;
+
+ /** Queue to store LoggerDTO objects, with a capacity of 100,000 */
private ArrayBlockingQueue myQueue = null;
+
+ /** Lock object used for synchronization (currently not in use) */
private static final Object myLock = new Object();
+ /** Counter for failed attempts to add logs to the queue */
@Getter
private int countFalse = 0;
+ /** Counter for successful attempts to add logs to the queue */
@Getter
private int countSuccess = 0;
+ /**
+ * Returns the singleton instance of LoggerQueue. If the instance is null, a new
+ * one is created.
+ *
+ * @return the singleton instance of LoggerQueue
+ */
public static LoggerQueue getInstance() {
if (mMe == null) {
mMe = new LoggerQueue();
@@ -43,18 +63,39 @@ public static LoggerQueue getInstance() {
return mMe;
}
+ /**
+ * Private constructor to initialize the queue with a fixed capacity of 100,000.
+ * This ensures the class follows the singleton pattern.
+ */
private LoggerQueue() {
myQueue = new ArrayBlockingQueue<>(100000) {};
}
+ /**
+ * Clears all elements from the queue.
+ */
public void clearQueue() {
myQueue.clear();
}
+ /**
+ * Retrieves and removes the head of the queue, or returns null if the queue is
+ * empty.
+ *
+ * @return the head of the queue, or null if the queue is empty
+ */
public LoggerDTO getQueue() {
return myQueue.poll();
}
+ /**
+ * Adds a LoggerDTO task to the queue. Increments countSuccess if successful,
+ * otherwise increments countFalse.
+ *
+ * @param task
+ * the LoggerDTO task to add to the queue
+ * @return true if the task was successfully added, false otherwise
+ */
public boolean addQueue(LoggerDTO task) {
if (myQueue.add(task)) {
countSuccess++;
@@ -64,6 +105,34 @@ public boolean addQueue(LoggerDTO task) {
return false;
}
+ /**
+ * Adds a LoggerDTO task to the queue with detailed parameters. This method
+ * wraps the parameters into a LoggerDTO object and adds it to the queue.
+ *
+ * @param contextRef
+ * the Reactor context reference
+ * @param newSpan
+ * the span from Sleuth tracing
+ * @param service
+ * the service name
+ * @param startTime
+ * the start time of the operation
+ * @param endTime
+ * the end time of the operation
+ * @param result
+ * the result of the operation ("0" for success, "1" for failure)
+ * @param obj
+ * additional information about the result
+ * @param logType
+ * the type of log
+ * @param actionType
+ * the type of action being logged
+ * @param args
+ * the method arguments
+ * @param title
+ * a custom title for the log entry
+ * @return true if the task was successfully added, false otherwise
+ */
public boolean addQueue(
AtomicReference contextRef,
Span newSpan,
@@ -83,11 +152,18 @@ public boolean addQueue(
return true;
}
} catch (Exception ex) {
+ log.error("Failed to add log to the queue", ex);
}
countFalse++;
return false;
}
+ /**
+ * Retrieves a list of up to 100,000 LoggerDTO objects from the queue. The queue
+ * is drained to the provided list.
+ *
+ * @return a list of LoggerDTO objects
+ */
public List getRecords() {
List records = new ArrayList<>();
if (myQueue != null) {
@@ -96,10 +172,19 @@ public List getRecords() {
return records;
}
+ /**
+ * Returns the current size of the queue.
+ *
+ * @return the number of elements in the queue
+ */
public int getQueueSize() {
return myQueue.size();
}
+ /**
+ * Resets the counters for successful and failed attempts to add logs to the
+ * queue.
+ */
public void resetCount() {
countSuccess = 0;
countFalse = 0;
diff --git a/src/main/java/io/hoangtien2k3/commons/annotations/logging/LoggerSchedule.java b/src/main/java/io/hoangtien2k3/commons/annotations/logging/LoggerSchedule.java
index 615b5a1..fe62495 100644
--- a/src/main/java/io/hoangtien2k3/commons/annotations/logging/LoggerSchedule.java
+++ b/src/main/java/io/hoangtien2k3/commons/annotations/logging/LoggerSchedule.java
@@ -35,34 +35,111 @@
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
+/**
+ * The {@code LoggerSchedule} class is a Spring-managed configuration class
+ * responsible for processing and logging records from the {@link LoggerQueue}.
+ * This class schedules a task that runs periodically to extract log data,
+ * process it, and log it using a performance-specific logger. The log data
+ * includes information such as trace IDs, IP addresses, request IDs, input
+ * arguments, responses, and more.
+ *
+ *
+ * This class is designed to handle log records in a non-blocking, reactive web
+ * application environment, typically using Spring WebFlux. It extracts useful
+ * information from the application context and logs it for performance
+ * monitoring and debugging purposes.
+ *
+ *
+ *
Example Usage:
+ *
+ *
+ * {
+ * @code
+ * @Configuration
+ * @RequiredArgsConstructor
+ * @Slf4j
+ * public class LoggerSchedule {
+ *
+ * // Scheduled task to process log records every 3 seconds
+ * @Scheduled(fixedDelay = 3000)
+ * public void scheduleSaveLogClick() {
+ * // implementation
+ * }
+ *
+ * // Additional methods for processing records
+ * }
+ * }
+ *
+ *
+ *
+ * This class is typically used in conjunction with a log queue manager like
+ * {@link LoggerQueue} that accumulates log data asynchronously from various
+ * parts of the application. The scheduled task then retrieves and processes
+ * these logs at regular intervals.
+ *
+ *
+ *
Thread Safety:
+ *
+ * This class is thread-safe, as the scheduled method
+ * {@link #scheduleSaveLogClick()} is executed periodically in a controlled
+ * manner by the Spring framework's task scheduler. Internally, it relies on the
+ * {@link LoggerQueue}, which is backed by a thread-safe queue implementation.
+ *
+ */
@Configuration
@RequiredArgsConstructor
@Slf4j
public class LoggerSchedule {
+
+ /**
+ * Logger instance specifically used for logging performance-related
+ * information. This logger is configured separately from the main application
+ * logger to focus on performance metrics.
+ */
private static final Logger logPerf = LoggerFactory.getLogger("perfLogger");
+ /**
+ * Scheduled task that runs every 3 seconds to process the log records in the
+ * {@link LoggerQueue}.
+ *
+ * This method retrieves log records from the queue, processes each record, and
+ * logs the details. If an error occurs while processing a record, it is caught
+ * and logged separately. After processing, the method resets the counters for
+ * successful and failed log additions to the queue.
+ *
+ */
@Scheduled(fixedDelay = 3000)
public void scheduleSaveLogClick() {
long analyId = System.currentTimeMillis();
int numSuccess = 0;
int numFalse = 0;
List records = LoggerQueue.getInstance().getRecords();
+
for (LoggerDTO record : records) {
try {
process(record);
numSuccess++;
} catch (Exception e) {
numFalse++;
- log.error("Error while handle record queue: ", e.getMessage());
+ log.error("Error while handling record in the queue: ", e.getMessage());
}
}
- // log.info("AsyncLog analyId {}: QueueSize: {}, addSuccess: {}, addFalse: {},
- // writeSuccess:{}, writeFalse:{}",
- // analyId, records.size(), LoggerQueue.getInstance().getCountSuccess(),
- // LoggerQueue.getInstance().getCountFalse(), numSuccess, numFalse);
+
LoggerQueue.getInstance().resetCount();
}
+ /**
+ * Processes a single {@link LoggerDTO} record by extracting relevant
+ * information and logging it.
+ *
+ * This method handles trace IDs, IP addresses, request IDs, input arguments,
+ * responses, and more. It also applies necessary truncations to the input and
+ * output data to ensure they don't exceed the maximum allowed size.
+ *
+ *
+ * @param jwt
+ * the {@link Jwt} token to convert
+ * @return a {@link Collection} of {@link GrantedAuthority} derived from the JWT
+ * token
+ */
@Override
public Collection convert(Jwt jwt) {
var realmRoles = realmRoles(jwt);
@@ -55,10 +78,32 @@ public Collection convert(Jwt jwt) {
return authorities;
}
+ /**
+ * Extracts default granted authorities from the provided {@link Jwt} token
+ * using the {@link JwtGrantedAuthoritiesConverter}.
+ *
+ * If the default converter does not provide any authorities, an empty set is
+ * returned.
+ *
+ *
+ * @param jwt
+ * the {@link Jwt} token to extract default authorities from
+ * @return a {@link Collection} of default {@link GrantedAuthority}
+ */
private Collection defaultGrantedAuthorities(Jwt jwt) {
return Optional.ofNullable(defaultAuthoritiesConverter.convert(jwt)).orElse(emptySet());
}
+ /**
+ * Extracts realm roles from the provided {@link Jwt} token.
+ *
+ * Realm roles are found in the "realm_access" claim of the JWT token.
+ *
+ *
+ * @param jwt
+ * the {@link Jwt} token to extract realm roles from
+ * @return a {@link List} of realm role names
+ */
@SuppressWarnings("unchecked")
protected List realmRoles(Jwt jwt) {
return Optional.ofNullable(jwt.getClaimAsMap(CLAIM_REALM_ACCESS))
@@ -66,6 +111,20 @@ protected List realmRoles(Jwt jwt) {
.orElse(emptyList());
}
+ /**
+ * Extracts client-specific roles from the provided {@link Jwt} token for a
+ * specific client.
+ *
+ * Client-specific roles are found in the "resource_access" claim of the JWT
+ * token under the specified client ID.
+ *
+ *
+ * @param jwt
+ * the {@link Jwt} token to extract client roles from
+ * @param clientId
+ * the client ID to find roles for
+ * @return a {@link List} of client-specific role names
+ */
@SuppressWarnings("unchecked")
protected List clientRoles(Jwt jwt, String clientId) {
if (ObjectUtils.isEmpty(clientId)) {
diff --git a/src/main/java/io/hoangtien2k3/commons/constants/CommonErrorCode.java b/src/main/java/io/hoangtien2k3/commons/constants/CommonErrorCode.java
index 8c19e65..df0c4c5 100644
--- a/src/main/java/io/hoangtien2k3/commons/constants/CommonErrorCode.java
+++ b/src/main/java/io/hoangtien2k3/commons/constants/CommonErrorCode.java
@@ -14,25 +14,90 @@
*/
package io.hoangtien2k3.commons.constants;
+/**
+ * The `CommonErrorCode` class contains common error codes used throughout the
+ * application. These error codes are defined as constant strings for easy
+ * management and usage across the application.
+ */
public class CommonErrorCode {
+
+ /**
+ * Error code for invalid requests.
+ */
public static final String BAD_REQUEST = "bad_request";
+
+ /**
+ * Error code when a resource is not found.
+ */
public static final String NOT_FOUND = "not_found";
+
+ /**
+ * Error code for invalid parameters.
+ */
public static final String INVALID_PARAMS = "invalid_params";
+
+ /**
+ * Error code when data already exists.
+ */
public static final String EXIST_DATA = "exist_data";
+
+ /**
+ * Error code when data does not exist.
+ */
public static final String NOT_EXIST_DATA = "not_exist_data";
+
+ /**
+ * Error code for internal server errors.
+ */
public static final String INTERNAL_SERVER_ERROR = "internal_error";
+
+ /**
+ * Error code when the user is not authorized.
+ */
public static final String UN_AUTHORIZATION = "un_auth";
+
+ /**
+ * Error code when the user does not have permission.
+ */
public static final String NO_PERMISSION = "no_permission";
+
+ /**
+ * Error code when access is denied.
+ */
public static final String ACCESS_DENIED = "access_denied";
+
+ /**
+ * Error code when there is an error parsing the token.
+ */
public static final String PARSE_TOKEN_ERROR = "parse_token_failed";
+
+ /**
+ * Error code for SQL-related errors.
+ */
public static final String SQL_ERROR = "sql";
- public static final String TRUST_MST_01 = "trust_mst_01";
- public static final String TRUST_MST_02 = "trust_mst_02";
- public static final String COMPANY_NOT_FOUND_TRUST_INDENTITY = "company_not_found_trust_indentity";
+
+ /**
+ * Error code when group information is not found.
+ */
public static final String GROUP_INFO_NOT_FOUND = "group_info_not_found";
- public static final String GROUP_INFO_NOT_FOUND_2 = "group_info_not_found_2";
+
+ /**
+ * Error code indicating success.
+ */
public static final String SUCCESS = "success";
+
+ /**
+ * Error code when deserialization fails.
+ */
public static final String UN_DESERIALIZE = "un_deserialize";
+
+ /**
+ * Error code when there is a fault in password hashing.
+ */
public static final String HASHING_PASSWORD_FAULT = "hashing_password_fault";
+
+ /**
+ * Error code when an exception occurs during a SOAP call.
+ */
public static final String CALL_SOAP_ERROR = "Exception when call soap: ";
}
diff --git a/src/main/java/io/hoangtien2k3/commons/constants/Role.java b/src/main/java/io/hoangtien2k3/commons/constants/Role.java
index c071dec..dc0c831 100644
--- a/src/main/java/io/hoangtien2k3/commons/constants/Role.java
+++ b/src/main/java/io/hoangtien2k3/commons/constants/Role.java
@@ -14,7 +14,33 @@
*/
package io.hoangtien2k3.commons.constants;
+/**
+ * Enumeration representing different user roles in the application.
+ *
+ * This enum is used to define and manage various user roles and their
+ * associated permissions.
+ *
+ *
+ * Each constant in this enum corresponds to a specific role that a user can
+ * have within the system.
+ *
+ */
public enum Role {
+ /**
+ * Represents an administrative role with the highest level of permissions.
+ *
+ * Users with this role typically have full access to all system resources and
+ * management capabilities.
+ *
+ */
ROLE_admin,
+
+ /**
+ * Represents a standard user role with limited permissions.
+ *
+ * Users with this role typically have access to basic functionalities and
+ * resources, but not administrative controls.
+ *
+ */
ROLE_user
}
diff --git a/src/main/java/io/hoangtien2k3/commons/exception/UnRetryableException.java b/src/main/java/io/hoangtien2k3/commons/exception/UnRetryableException.java
index 06261c4..82f9768 100644
--- a/src/main/java/io/hoangtien2k3/commons/exception/UnRetryableException.java
+++ b/src/main/java/io/hoangtien2k3/commons/exception/UnRetryableException.java
@@ -18,11 +18,32 @@
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
+/**
+ * Exception class representing an error that cannot be retried.
+ *
+ * This exception extends {@link BusinessException} and is used to signal errors
+ * that are not eligible for retrying, indicating that the error condition is
+ * permanent and requires manual intervention or resolution.
+ *
+ *
+ * The exception carries an error code and a message to provide details about
+ * the error.
+ *
+ */
@EqualsAndHashCode(callSuper = true)
@Data
@NoArgsConstructor
public class UnRetryableException extends BusinessException {
+ /**
+ * Constructs a new {@code UnRetryableException} with the specified error code
+ * and message.
+ *
+ * @param errorCode
+ * the error code associated with the exception.
+ * @param message
+ * the detailed message explaining the cause of the exception.
+ */
public UnRetryableException(String errorCode, String message) {
super(errorCode, message);
}
diff --git a/src/main/java/io/hoangtien2k3/commons/factory/ObjectMapperFactory.java b/src/main/java/io/hoangtien2k3/commons/factory/ObjectMapperFactory.java
index af02881..a349437 100644
--- a/src/main/java/io/hoangtien2k3/commons/factory/ObjectMapperFactory.java
+++ b/src/main/java/io/hoangtien2k3/commons/factory/ObjectMapperFactory.java
@@ -24,11 +24,151 @@
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import java.io.IOException;
+/**
+ * Factory class for creating and configuring {@link ObjectMapper} instances.
+ *
+ * The {@code ObjectMapperFactory} class provides methods to obtain different
+ * configurations of Jackson's {@link ObjectMapper}. It includes configurations
+ * for handling JSON deserialization, serialization, and custom deserializers.
+ *
+ *
+ *
Class Overview:
+ *
+ * This class provides static methods to retrieve {@link ObjectMapper} instances
+ * with different configurations: - getInstance: Provides a
+ * singleton instance with custom configurations. - getInstance2:
+ * Provides a second instance with a different configuration. -
+ * defaultGetInstance: Provides a default instance with standard
+ * configurations.
+ *
+ *
+ *
Methods:
+ *
+ *
getInstance: Provides a singleton {@link ObjectMapper}
+ * instance with custom configurations.
+ *
+ *
Returns:
+ *
+ * A singleton instance of {@link ObjectMapper} configured with custom settings
+ * such as ignoring unknown properties, accepting single values as arrays,
+ * unwrapping single value arrays, and registering custom deserializers.
+ *
defaultGetInstance: Provides a default
+ * {@link ObjectMapper} instance with standard configurations.
+ *
+ *
Returns:
+ *
+ * A new instance of {@link ObjectMapper} configured to ignore unknown
+ * properties, accept single values as arrays, include non-null values only, and
+ * register available modules.
+ *
+ * This factory class is designed to provide different configurations for
+ * Jackson's {@link ObjectMapper} based on the needs of different use cases.
+ * Custom deserializers and module registrations are handled within the factory
+ * to ensure consistent configuration across various parts of the application.
+ *
+ */
public class ObjectMapperFactory {
private static ObjectMapper objectMapper;
private static ObjectMapper objectMapperV2 = new ObjectMapper();
private static ObjectMapper defaultGetInstance = new ObjectMapper();
+ /**
+ * Provides a singleton {@link ObjectMapper} instance with custom
+ * configurations.
+ *
+ * Configures the instance to ignore unknown properties, accept single values as
+ * arrays, unwrap single value arrays, and register custom deserializers.
+ *
+ *
+ * @return A singleton instance of {@link ObjectMapper} with custom settings.
+ */
public static ObjectMapper getInstance() {
if (objectMapper == null) {
objectMapper = new ObjectMapper();
@@ -44,12 +184,32 @@ public static ObjectMapper getInstance() {
return objectMapper;
}
+ /**
+ * Provides a second {@link ObjectMapper} instance with alternative
+ * configurations.
+ *
+ * Configures the instance to ignore unknown properties and accept single values
+ * as arrays.
+ *
+ *
+ * @return A new instance of {@link ObjectMapper} with alternative settings.
+ */
public static ObjectMapper getInstance2() {
objectMapperV2.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapperV2.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
return objectMapperV2;
}
+ /**
+ * Provides a default {@link ObjectMapper} instance with standard
+ * configurations.
+ *
+ * Configures the instance to ignore unknown properties, accept single values as
+ * arrays, include non-null values only, and register all available modules.
+ *
+ *
+ * @return A new instance of {@link ObjectMapper} with standard settings.
+ */
public static ObjectMapper defaultGetInstance() {
defaultGetInstance.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
defaultGetInstance.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
@@ -58,6 +218,9 @@ public static ObjectMapper defaultGetInstance() {
return defaultGetInstance;
}
+ /**
+ * Custom deserializer for handling numeric and boolean values.
+ */
private static class NumericBooleanDeserializer extends JsonDeserializer {
@Override
public Boolean deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
diff --git a/src/main/java/io/hoangtien2k3/commons/factory/UnmarshallerFactory.java b/src/main/java/io/hoangtien2k3/commons/factory/UnmarshallerFactory.java
index 939917e..eb764b6 100644
--- a/src/main/java/io/hoangtien2k3/commons/factory/UnmarshallerFactory.java
+++ b/src/main/java/io/hoangtien2k3/commons/factory/UnmarshallerFactory.java
@@ -21,11 +21,89 @@
import javax.xml.bind.Unmarshaller;
import lombok.extern.slf4j.Slf4j;
+/**
+ * Factory class for creating and caching {@link Unmarshaller} instances.
+ *
+ * The {@code UnmarshallerFactory} class provides a method to obtain a singleton
+ * {@link Unmarshaller} instance for a given class. It utilizes a cache to
+ * ensure that each {@link Unmarshaller} is created only once per class,
+ * improving performance and reducing the overhead of creating multiple
+ * instances.
+ *
+ *
+ *
Class Overview:
+ *
+ * This class provides a static method to retrieve {@link Unmarshaller}
+ * instances, which are cached for efficiency.
+ *
+ *
+ *
Methods:
+ *
+ *
getInstance: Retrieves or creates an
+ * {@link Unmarshaller} instance for the specified class.
+ *
+ *
Parameters:
+ *
+ *
clz (Class): The class for which the
+ * {@link Unmarshaller} is to be created or retrieved.
+ *
+ *
+ *
Returns:
+ *
+ * An {@link Unmarshaller} instance for the specified class, or
+ * null if an error occurs during creation.
+ *
+ *
+ *
Exceptions:
+ *
+ *
JAXBException: Thrown if an error occurs during the creation
+ * of the {@link Unmarshaller}.
+ *
+ *
+ *
+ *
+ *
+ *
+ *
Private Fields:
+ *
+ *
instance: A Map that
+ * caches {@link Unmarshaller} instances.
+ * This factory class uses a cache to ensure that {@link Unmarshaller} instances
+ * are only created once per class, thus optimizing the performance of XML
+ * unmarshalling operations.
+ *
+ */
@Slf4j
public class UnmarshallerFactory {
private static Map instance = new HashMap<>();
+ /**
+ * Retrieves or creates an {@link Unmarshaller} instance for the specified
+ * class.
+ *
+ * If an {@link Unmarshaller} for the given class is already cached, it is
+ * returned. Otherwise, a new {@link Unmarshaller} is created and cached for
+ * future use.
+ *
+ *
+ * @param clz
+ * The class for which the {@link Unmarshaller} is to be created or
+ * retrieved.
+ * @return An {@link Unmarshaller} instance for the specified class, or
+ * null if an error occurs.
+ */
public static Unmarshaller getInstance(Class clz) {
Unmarshaller obj = instance.get(clz);
if (obj != null) return obj;
diff --git a/src/main/java/io/hoangtien2k3/commons/filter/http/GatewayContextFilter.java b/src/main/java/io/hoangtien2k3/commons/filter/http/GatewayContextFilter.java
index 5a2a139..0a02b72 100644
--- a/src/main/java/io/hoangtien2k3/commons/filter/http/GatewayContextFilter.java
+++ b/src/main/java/io/hoangtien2k3/commons/filter/http/GatewayContextFilter.java
@@ -46,19 +46,80 @@
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
+/**
+ * A filter that captures and processes HTTP request and response data for
+ * logging purposes in a Spring WebFlux gateway application.
+ *
+ * This filter intercepts incoming requests and outgoing responses to record
+ * their headers and content, if logging is enabled for them. The context is
+ * stored in the {@link GatewayContext}, which can be accessed later in the
+ * request handling pipeline.
+ *
+ *
+ *
+ * Note: This filter is only active in non-production profiles
+ * (i.e., when the profile is not "prod").
+ *
+ *
+ *
+ * Logging and context management:
+ *
+ *
+ *
Logs request and response data based on configurations in
+ * {@link HttpLogProperties}.
+ *
Stores the captured data in a {@link GatewayContext} object, which is
+ * saved in the {@link ServerWebExchange} attributes.
+ *
Supports logging of JSON and form URL-encoded content types.
+ *
+ *
+ *
+ * The filter is executed with the highest precedence in the filter chain,
+ * ensuring that it captures all relevant data before other filters process the
+ * request.
+ *
+ *
+ * @author hoangtien2k3
+ * @see WebFilter
+ * @see Ordered
+ * @see GatewayContext
+ */
@Component
@Log4j2
@AllArgsConstructor
@Profile("!prod")
public class GatewayContextFilter implements WebFilter, Ordered {
- private HttpLogProperties httpLogProperties;
- private CodecConfigurer codecConfigurer;
+ /**
+ * Properties for configuring HTTP request and response logging.
+ */
+ private final HttpLogProperties httpLogProperties;
+
+ /**
+ * Configurer for customizing HTTP message readers and writers.
+ */
+ private final CodecConfigurer codecConfigurer;
+ /**
+ * Specifies the order in which this filter is applied. This filter has the
+ * highest precedence.
+ *
+ * @return the highest precedence value, indicating that this filter is applied
+ * first.
+ */
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
+ /**
+ * Filters the incoming HTTP request and outgoing response, capturing data for
+ * logging.
+ *
+ * @param exchange
+ * the current server exchange
+ * @param chain
+ * the web filter chain
+ * @return {@link Mono} signaling when request processing is complete
+ */
@Override
public Mono filter(ServerWebExchange exchange, WebFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
@@ -98,11 +159,23 @@ public Mono filter(ServerWebExchange exchange, WebFilterChain chain) {
}
/**
- * ReadFormData
+ * Reads form data from the incoming HTTP request, processes it, and prepares
+ * the data for further filtering in the chain.
+ *
+ * This method handles form data (typically from an HTML form) in the request
+ * body. It captures the form data, encodes it properly, and rewrites the
+ * request body with the processed form data before passing the request along
+ * the filter chain.
+ *
*
* @param exchange
+ * the current server exchange containing the request and response
* @param chain
- * @return
+ * the web filter chain
+ * @param gatewayContext
+ * the context object that stores request and response data for
+ * logging
+ * @return a {@link Mono} signaling when request processing is complete
*/
private Mono readFormData(ServerWebExchange exchange, WebFilterChain chain, GatewayContext gatewayContext) {
HttpHeaders headers = exchange.getRequest().getHeaders();
@@ -200,11 +273,23 @@ public Flux getBody() {
}
/**
- * ReadJsonBody
+ * Reads JSON data from the incoming HTTP request body and prepares it for
+ * further filtering in the chain.
+ *
+ * This method handles JSON data in the request body by capturing and storing it
+ * in the {@link GatewayContext}. It then decorates the request to allow the
+ * request body to be read multiple times before passing the request along the
+ * filter chain.
+ *
*
* @param exchange
+ * the current server exchange containing the request and response
* @param chain
- * @return
+ * the web filter chain
+ * @param gatewayContext
+ * the context object that stores request and response data for
+ * logging
+ * @return a {@link Mono} signaling when request processing is complete
*/
private Mono readBody(ServerWebExchange exchange, WebFilterChain chain, GatewayContext gatewayContext) {
return DataBufferUtils.join(exchange.getRequest().getBody()).flatMap(dataBuffer -> {
diff --git a/src/main/java/io/hoangtien2k3/commons/filter/http/PerformanceLogFilter.java b/src/main/java/io/hoangtien2k3/commons/filter/http/PerformanceLogFilter.java
index 8f4da64..a3b5ec3 100644
--- a/src/main/java/io/hoangtien2k3/commons/filter/http/PerformanceLogFilter.java
+++ b/src/main/java/io/hoangtien2k3/commons/filter/http/PerformanceLogFilter.java
@@ -21,6 +21,7 @@
import io.hoangtien2k3.commons.model.GatewayContext;
import io.hoangtien2k3.commons.utils.DataUtil;
import io.hoangtien2k3.commons.utils.RequestUtils;
+import io.hoangtien2k3.commons.utils.TruncateUtils;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
@@ -44,6 +45,59 @@
import reactor.core.publisher.Mono;
import reactor.util.context.Context;
+/**
+ * A WebFlux filter that logs performance metrics and request/response data for
+ * HTTP requests. This filter is applied to all incoming requests, except those
+ * targeting actuator endpoints. It measures the time taken to process requests,
+ * captures tracing information using Sleuth, and conditionally logs request and
+ * response data based on the application's active profile.
+ *
+ * The performance data is logged using a dedicated logger (`perfLogger`), and
+ * request/response data is logged using a separate logger (`reqResLogger`).
+ *
+ *
Key Features:
+ *
+ *
Logs the duration of HTTP request processing.
+ *
Integrates with Spring Cloud Sleuth for distributed tracing.
+ *
Logs request and response bodies in non-production environments.
+ */
@Component
@RequiredArgsConstructor
public class PerformanceLogFilter implements WebFilter, Ordered {
@@ -53,6 +107,30 @@ public class PerformanceLogFilter implements WebFilter, Ordered {
private static final int MAX_BYTE = 800; // Max byte allow to print
private final Environment environment;
+ /**
+ * Filters the incoming HTTP request to log performance metrics and optionally
+ * logs request/response data if the environment is not production.
+ *
+ *
Processing Flow:
+ *
+ *
Records the start time of the request.
+ *
Creates a new tracing span using Sleuth's Tracer.
+ *
If the request targets an actuator endpoint, it bypasses logging and
+ * continues the chain.
+ *
Filters the request through the chain, logging performance data upon
+ * success or failure.
+ *
If the environment is not production, logs the request and response
+ * bodies.
+ *
+ *
+ * @param exchange
+ * The current server web exchange containing the request and
+ * response.
+ * @param chain
+ * The WebFilterChain to pass the request to the next filter.
+ * @return A {@code Mono} that completes when the filter chain has
+ * completed.
+ */
@Override
public Mono filter(ServerWebExchange exchange, WebFilterChain chain) {
long startMillis = System.currentTimeMillis();
@@ -72,8 +150,7 @@ public Mono filter(ServerWebExchange exchange, WebFilterChain chain) {
logPerf(exchange, newSpan, name, startMillis, "F", null);
})
.contextWrite(context -> {
- var currContext = (Context) context;
- contextRef.set(currContext);
+ contextRef.set((Context) context);
// the error happens in a different thread, so get the trace from context, set
// in MDC
// and downstream
@@ -218,7 +295,7 @@ private String truncateBody(MultiValueMap formData) {
StringBuilder messageResponse = new StringBuilder();
Set keys = formData.keySet();
for (String key : keys) {
- messageResponse.append(key + ":" + truncateBody(formData.get(key)));
+ messageResponse.append(key).append(":").append(truncateBody(formData.get(key)));
}
return messageResponse.toString();
}
@@ -232,65 +309,11 @@ private String truncateBody(List messageList) {
}
private String truncateBody(String s, int maxByte) {
- int b = 0;
- for (int i = 0; i < s.length(); i++) {
- char c = s.charAt(i);
-
- // ranges from http://en.wikipedia.org/wiki/UTF-8
- int skip = 0;
- int more;
- if (c <= 0x007f) {
- more = 1;
- } else if (c <= 0x07FF) {
- more = 2;
- } else if (c <= 0xd7ff) {
- more = 3;
- } else if (c <= 0xDFFF) {
- // surrogate area, consume next char as well
- more = 4;
- skip = 1;
- } else {
- more = 3;
- }
-
- if (b + more > maxByte) {
- return s.substring(0, i);
- }
- b += more;
- i += skip;
- }
- return s;
+ return TruncateUtils.truncateBody(s, maxByte);
}
private String truncateBody(String s) {
- int b = 0;
- for (int i = 0; i < s.length(); i++) {
- char c = s.charAt(i);
-
- // ranges from http://en.wikipedia.org/wiki/UTF-8
- int skip = 0;
- int more;
- if (c <= 0x007f) {
- more = 1;
- } else if (c <= 0x07FF) {
- more = 2;
- } else if (c <= 0xd7ff) {
- more = 3;
- } else if (c <= 0xDFFF) {
- // surrogate area, consume next char as well
- more = 4;
- skip = 1;
- } else {
- more = 3;
- }
-
- if (b + more > MAX_BYTE) {
- return s.substring(0, i);
- }
- b += more;
- i += skip;
- }
- return s;
+ return TruncateUtils.truncateBody(s, MAX_BYTE);
}
/**
diff --git a/src/main/java/io/hoangtien2k3/commons/filter/http/ResponseLogFilter.java b/src/main/java/io/hoangtien2k3/commons/filter/http/ResponseLogFilter.java
index 19a7301..a1c7f1d 100644
--- a/src/main/java/io/hoangtien2k3/commons/filter/http/ResponseLogFilter.java
+++ b/src/main/java/io/hoangtien2k3/commons/filter/http/ResponseLogFilter.java
@@ -43,15 +43,65 @@
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
+/**
+ * A WebFlux filter for logging HTTP response bodies in a Spring Gateway
+ * application. This filter decorates the response object to capture and log the
+ * response body content. It is typically used in non-production environments to
+ * help with debugging and monitoring.
+ *
+ * The filter can handle large response bodies by configuring the maximum
+ * in-memory size, and it supports both Mono and Flux types of responses.
+ *
+ *
Key Features:
+ *
+ *
Logs HTTP response bodies for requests that match certain criteria.
+ *
Decorates the response object to intercept and modify the response
+ * body.
+ *
Supports both single (Mono) and multiple (Flux) data buffers.
+ *
Configurable in-memory buffer size for handling large responses.
+ */
@Log4j2
@AllArgsConstructor
@Component
// @Profile("!prod")
public class ResponseLogFilter implements WebFilter, Ordered {
+
private final ExchangeStrategies exchangeStrategies = ExchangeStrategies.builder()
.codecs(cl -> cl.defaultCodecs().maxInMemorySize(50 * 1024 * 1024))
.build();
+ /**
+ * Converts an InputStream to a byte array.
+ *
+ * @param inStream
+ * the InputStream to convert
+ * @return the byte array representation of the input stream
+ */
private static byte[] toByteArray(InputStream inStream) {
ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
byte[] buff = new byte[100];
@@ -68,6 +118,27 @@ private static byte[] toByteArray(InputStream inStream) {
return in_b;
}
+ /**
+ * Filters the incoming HTTP request and decorates the response to log the
+ * response body.
+ *
+ *
Processing Flow:
+ *
+ *
Checks if the request should log the response body based on the
+ * {@link GatewayContext} settings.
+ *
If logging is enabled, wraps the response in a
+ * {@link ServerHttpResponseDecorator}.
+ *
The decorator intercepts the response body, logs it, and returns a
+ * modified data buffer.
+ *
+ *
+ * @param exchange
+ * The current server web exchange containing the request and
+ * response.
+ * @param chain
+ * The WebFilterChain to pass the request to the next filter.
+ * @return A `Mono` that completes when the filter chain has completed.
+ */
@Override
public Mono filter(ServerWebExchange exchange, WebFilterChain chain) {
GatewayContext gatewayContext = exchange.getAttribute(GatewayContext.CACHE_GATEWAY_CONTEXT);
@@ -101,6 +172,16 @@ public Mono writeAndFlushWith(Publisher extends Publisher extends Data
return chain.filter(exchange.mutate().response(responseDecorator).build());
}
+ /**
+ * Logs the content of the response body. This method is called within the
+ * decorated response object to capture the response data as it is written.
+ *
+ * @param buffer
+ * The DataBuffer containing the response data.
+ * @param exchange
+ * The current server web exchange.
+ * @return A DataBuffer containing the logged data.
+ */
private DataBuffer logRequestBody(DataBuffer buffer, ServerWebExchange exchange) {
InputStream dataBuffer = buffer.asInputStream();
byte[] bytes = toByteArray(dataBuffer);
@@ -111,16 +192,36 @@ private DataBuffer logRequestBody(DataBuffer buffer, ServerWebExchange exchange)
return nettyDataBufferFactory.wrap(bytes);
}
+ /**
+ * Specifies the order in which this filter is applied relative to other
+ * filters. Filters with a lower order value are applied first.
+ *
+ * @return The order value for this filter.
+ */
@Override
public int getOrder() {
return 6;
}
+ /**
+ * A wrapper class that adapts a response body Publisher to a
+ * {@link ClientHttpResponse}. This class is used to facilitate logging by
+ * allowing the response body to be accessed and logged before it is returned to
+ * the client.
+ */
public static class ResponseAdapter implements ClientHttpResponse {
private final Flux flux;
private final HttpHeaders headers;
+ /**
+ * Constructs a ResponseAdapter with the given body and headers.
+ *
+ * @param body
+ * The Publisher that provides the response body.
+ * @param headers
+ * The headers associated with the response.
+ */
public ResponseAdapter(Publisher extends DataBuffer> body, HttpHeaders headers) {
this.headers = headers;
if (body instanceof Flux) {
diff --git a/src/main/java/io/hoangtien2k3/commons/filter/properties/KeyCloakProperties.java b/src/main/java/io/hoangtien2k3/commons/filter/properties/KeyCloakProperties.java
index 3fe8701..1fa9150 100644
--- a/src/main/java/io/hoangtien2k3/commons/filter/properties/KeyCloakProperties.java
+++ b/src/main/java/io/hoangtien2k3/commons/filter/properties/KeyCloakProperties.java
@@ -18,6 +18,74 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * The `KeyCloakProperties` class is used to store configuration properties
+ * related to Keycloak. It is a simple POJO (Plain Old Java Object) with Lombok
+ * annotations to reduce boilerplate code such as getters, setters, and
+ * constructors.
+ *
+ *
Attributes:
+ *
+ *
clientId: A string representing the client ID used in
+ * Keycloak. This is typically used to identify the client application.
+ *
clientSecret: A string representing the client secret
+ * used in Keycloak. This secret is used to authenticate the client application
+ * with Keycloak.
+ *
+ *
+ *
Lombok Annotations:
+ *
+ *
@Data: Generates boilerplate code such as getters,
+ * setters, `equals()`, `hashCode()`, and `toString()` methods
+ * automatically.
+ *
@AllArgsConstructor: Generates a constructor with all
+ * fields as parameters. This allows you to create an instance of the class with
+ * all attributes set at once.
+ *
@NoArgsConstructor: Generates a no-arguments
+ * constructor. This is useful for creating instances of the class without
+ * setting any attributes initially.
+ *
+ * In this example, the `KeyCloakProperties` class is used to bind the
+ * configuration properties from `application.yml` file. The `KeyCloakConfig`
+ * class demonstrates how to use these properties within a Spring Boot
+ * application.
+ */
@Data
@AllArgsConstructor
@NoArgsConstructor
diff --git a/src/main/java/io/hoangtien2k3/commons/filter/properties/MonitoringProperties.java b/src/main/java/io/hoangtien2k3/commons/filter/properties/MonitoringProperties.java
index 07d5ae0..34f3e87 100644
--- a/src/main/java/io/hoangtien2k3/commons/filter/properties/MonitoringProperties.java
+++ b/src/main/java/io/hoangtien2k3/commons/filter/properties/MonitoringProperties.java
@@ -20,6 +20,87 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * The `MonitoringProperties` class holds configuration properties related to
+ * monitoring within an application. It uses Lombok annotations to automatically
+ * generate boilerplate code such as getters, setters, and constructors.
+ *
+ *
Attributes:
+ *
+ *
isEnable: A boolean flag indicating whether monitoring
+ * is enabled or not. Defaults to `true` if not explicitly set.
+ *
meterRegistry: An instance of `MeterRegistry` used for
+ * registering and managing metrics. Defaults to a `LoggingMeterRegistry` if not
+ * explicitly set.
@AllArgsConstructor: Creates a constructor with all
+ * fields as parameters, allowing for easy instantiation of the class with all
+ * properties set.
+ *
@NoArgsConstructor: Provides a no-argument constructor
+ * for creating instances of the class without initializing the fields.
+ * The `MonitoringProperties` class is used to configure monitoring settings for
+ * an application. The `isEnable` flag determines whether monitoring is active,
+ * while the `meterRegistry` attribute specifies the type of meter registry to
+ * be used for managing metrics.
+ *
+ *
+ *
+ * The default value for `isEnable` is `true`, meaning monitoring is enabled by
+ * default unless explicitly disabled.
+ *
+ *
+ *
+ * The `meterRegistry` is an instance of `MeterRegistry`, which is responsible
+ * for collecting and reporting metrics. The default value is an instance of
+ * `LoggingMeterRegistry`, which logs metrics to the console. You can replace
+ * this with a custom `MeterRegistry` implementation as needed.
+ *
+ */
@Data
@AllArgsConstructor
@NoArgsConstructor
diff --git a/src/main/java/io/hoangtien2k3/commons/filter/properties/PoolProperties.java b/src/main/java/io/hoangtien2k3/commons/filter/properties/PoolProperties.java
index f1fc15c..7b92b79 100644
--- a/src/main/java/io/hoangtien2k3/commons/filter/properties/PoolProperties.java
+++ b/src/main/java/io/hoangtien2k3/commons/filter/properties/PoolProperties.java
@@ -18,6 +18,96 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * The `PoolProperties` class encapsulates configuration settings for a
+ * connection pool. It uses Lombok annotations to generate boilerplate code such
+ * as getters, setters, and constructors.
+ *
+ *
Attributes:
+ *
+ *
maxSize: An integer representing the maximum number of
+ * connections that the pool can hold. Defaults to `2000` if not explicitly
+ * set.
+ *
maxPendingAcquire: An integer representing the maximum
+ * number of connections that can be pending acquisition from the pool. Defaults
+ * to `2000` if not explicitly set.
@AllArgsConstructor: Creates a constructor with all
+ * fields as parameters, allowing for easy instantiation of the class with all
+ * properties set.
+ *
@NoArgsConstructor: Provides a no-argument constructor
+ * for creating instances of the class with default values.
+ * The `PoolProperties` class is used to configure the parameters for a
+ * connection pool in an application. It allows you to specify the maximum size
+ * of the pool and the maximum number of connections that can be pending
+ * acquisition.
+ *
+ *
+ *
+ * The default value for `maxSize` is `2000`, which means the pool can hold up
+ * to 2000 connections. If you want to allow more or fewer connections, you can
+ * adjust this value.
+ *
+ *
+ *
+ * The `maxPendingAcquire` value is also set to `2000` by default. This property
+ * defines the maximum number of connections that can be in the process of being
+ * acquired from the pool. If the number of pending connections exceeds this
+ * limit, additional requests will have to wait until a connection becomes
+ * available.
+ *
+ *
+ *
+ * Both properties are customizable to fit the specific needs of your
+ * application's connection pool. Adjusting these values can help optimize
+ * performance and resource usage based on your application's requirements.
+ *
+ */
@Data
@AllArgsConstructor
@NoArgsConstructor
diff --git a/src/main/java/io/hoangtien2k3/commons/filter/properties/ProxyProperties.java b/src/main/java/io/hoangtien2k3/commons/filter/properties/ProxyProperties.java
index 7c0caf6..9753de8 100644
--- a/src/main/java/io/hoangtien2k3/commons/filter/properties/ProxyProperties.java
+++ b/src/main/java/io/hoangtien2k3/commons/filter/properties/ProxyProperties.java
@@ -18,6 +18,103 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * The `ProxyProperties` class is used to configure proxy settings for an
+ * application. It employs Lombok annotations to automatically generate getters,
+ * setters, and constructors.
+ *
+ *
Attributes:
+ *
+ *
enable: A boolean flag indicating whether proxy support
+ * is enabled. Defaults to `false` if not explicitly set.
+ *
httpHost: A string representing the host address for
+ * HTTP proxy configuration. This value should be set if an HTTP proxy is
+ * required.
+ *
httpPort: An integer specifying the port number for HTTP
+ * proxy configuration. This value should be set in conjunction with
+ * `httpHost`.
+ *
httpsHost: A string representing the host address for
+ * HTTPS proxy configuration. This value should be set if an HTTPS proxy is
+ * required.
+ *
httpsPort: An integer specifying the port number for
+ * HTTPS proxy configuration. This value should be set in conjunction with
+ * `httpsHost`.
+ *
+ *
+ *
Lombok Annotations:
+ *
+ *
@Data: Automatically generates getters, setters,
+ * `equals()`, `hashCode()`, and `toString()` methods for the class.
+ *
@AllArgsConstructor: Creates a constructor with all
+ * fields as parameters, allowing for easy instantiation with all properties
+ * set.
+ *
@NoArgsConstructor: Provides a no-argument constructor
+ * for creating instances with default values.
+ * The `ProxyProperties` class provides a way to configure proxy settings for
+ * your application, including both HTTP and HTTPS proxies. It is particularly
+ * useful when your application needs to communicate through a proxy server.
+ *
+ *
+ *
+ * By default, proxy support is disabled (i.e., `enable` is `false`). To use a
+ * proxy, you need to set the `enable` flag to `true` and provide the necessary
+ * `httpHost`, `httpPort`, `httpsHost`, and `httpsPort` values.
+ *
+ *
+ *
+ * The `httpHost` and `httpPort` define the proxy settings for HTTP connections,
+ * while the `httpsHost` and `httpsPort` define the settings for HTTPS
+ * connections. Make sure to configure these values appropriately based on your
+ * network infrastructure and proxy server settings.
+ *
+ */
@Data
@AllArgsConstructor
@NoArgsConstructor
diff --git a/src/main/java/io/hoangtien2k3/commons/filter/properties/RetryProperties.java b/src/main/java/io/hoangtien2k3/commons/filter/properties/RetryProperties.java
index ac4fb2f..abe8c2c 100644
--- a/src/main/java/io/hoangtien2k3/commons/filter/properties/RetryProperties.java
+++ b/src/main/java/io/hoangtien2k3/commons/filter/properties/RetryProperties.java
@@ -22,6 +22,112 @@
import lombok.NoArgsConstructor;
import org.springframework.http.HttpMethod;
+/**
+ * The `RetryProperties` class is used to configure retry behavior for HTTP
+ * requests within an application. It leverages Lombok annotations to
+ * automatically generate getters, setters, and constructors.
+ *
+ *
Attributes:
+ *
+ *
isEnable: A boolean flag indicating whether retry
+ * functionality is enabled. Defaults to `true` if not explicitly set.
+ *
count: An integer specifying the number of retry
+ * attempts. Defaults to `2` if not explicitly set.
+ *
methods: A list of HTTP methods for which retry should
+ * be applied. By default, it includes `HttpMethod.GET`, `HttpMethod.PUT`, and
+ * `HttpMethod.DELETE`.
+ *
exceptions: A list of exception classes that should
+ * trigger a retry. Defaults to `ConnectTimeoutException` and
+ * `ReadTimeoutException`.
+ *
+ *
+ *
Lombok Annotations:
+ *
+ *
@Data: Automatically generates getters, setters,
+ * `equals()`, `hashCode()`, and `toString()` methods for the class.
+ *
@AllArgsConstructor: Creates a constructor with all
+ * fields as parameters, allowing for easy instantiation with all properties
+ * set.
+ *
@NoArgsConstructor: Provides a no-argument constructor
+ * for creating instances with default values.
+ * The `RetryProperties` class provides configuration options for retrying HTTP
+ * requests when certain conditions are met. The `isEnable` flag determines
+ * whether the retry logic is active. The `count` attribute specifies how many
+ * times the request should be retried if a retryable exception occurs.
+ *
+ *
+ *
+ * The `methods` list specifies which HTTP methods the retry logic should apply
+ * to. By default, it includes `GET`, `PUT`, and `DELETE` requests, but this can
+ * be customized based on the application's requirements.
+ *
+ *
+ *
+ * The `exceptions` list includes exception classes that trigger a retry.
+ * Commonly used exceptions like `ConnectTimeoutException` and
+ * `ReadTimeoutException` are included by default, but this can be customized to
+ * handle other specific exceptions as needed.
+ *
+ *
+ *
+ * Using this configuration allows applications to handle transient errors and
+ * improve resilience by automatically retrying failed requests, which can be
+ * particularly useful in distributed systems or when dealing with unreliable
+ * network conditions.
+ *
+ */
@Data
@AllArgsConstructor
@NoArgsConstructor
diff --git a/src/main/java/io/hoangtien2k3/commons/filter/properties/TimeoutProperties.java b/src/main/java/io/hoangtien2k3/commons/filter/properties/TimeoutProperties.java
index 0bf972b..c2bba34 100644
--- a/src/main/java/io/hoangtien2k3/commons/filter/properties/TimeoutProperties.java
+++ b/src/main/java/io/hoangtien2k3/commons/filter/properties/TimeoutProperties.java
@@ -18,6 +18,87 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * The `TimeoutProperties` class is used to configure timeout settings for HTTP
+ * requests within an application. It leverages Lombok annotations to
+ * automatically generate getters, setters, and constructors.
+ *
+ *
Attributes:
+ *
+ *
read: An integer specifying the read timeout in
+ * milliseconds. The default value is `180000` milliseconds (3 minutes). This
+ * timeout controls how long the application will wait for data to be read from
+ * the server before timing out.
+ *
connection: An integer specifying the connection timeout
+ * in milliseconds. The default value is `500` milliseconds (0.5 seconds). This
+ * timeout controls how long the application will wait to establish a connection
+ * to the server before timing out.
+ *
+ *
+ *
Lombok Annotations:
+ *
+ *
@Data: Automatically generates getters, setters,
+ * `equals()`, `hashCode()`, and `toString()` methods for the class.
+ *
@AllArgsConstructor: Creates a constructor with all
+ * fields as parameters, allowing for easy instantiation with all properties
+ * set.
+ *
@NoArgsConstructor: Provides a no-argument constructor
+ * for creating instances with default values.
+ * The `TimeoutProperties` class provides configuration options for setting
+ * timeout values for HTTP requests. The `read` attribute specifies how long the
+ * client will wait for data to be read from the server before giving up. The
+ * `connection` attribute specifies how long the client will wait to establish a
+ * connection to the server.
+ *
+ *
+ *
+ * Using this configuration allows you to fine-tune how your application handles
+ * network delays and connection issues. By setting appropriate timeout values,
+ * you can improve the responsiveness of your application and handle network
+ * conditions more effectively.
+ *
+ */
@Data
@AllArgsConstructor
@NoArgsConstructor
diff --git a/src/main/java/io/hoangtien2k3/commons/filter/webclient/WebClientLoggingFilter.java b/src/main/java/io/hoangtien2k3/commons/filter/webclient/WebClientLoggingFilter.java
index adbe3e8..da45243 100644
--- a/src/main/java/io/hoangtien2k3/commons/filter/webclient/WebClientLoggingFilter.java
+++ b/src/main/java/io/hoangtien2k3/commons/filter/webclient/WebClientLoggingFilter.java
@@ -23,6 +23,105 @@
import org.springframework.web.reactive.function.client.ExchangeFunction;
import reactor.core.publisher.Mono;
+/**
+ * The {@code WebClientLoggingFilter} class is an implementation of
+ * {@link ExchangeFilterFunction} used to log HTTP request and response details
+ * when using Spring's {@link}. This filter logs information about API calls,
+ * including headers and bodies, with support for obfuscating sensitive header
+ * values. It utilizes Lombok annotations for logging and constructor injection.
+ *
+ *
Attributes:
+ *
+ *
OBFUSCATE_HEADER: A constant string used to replace
+ * sensitive header values in logs. The default value is "xxxxx".
+ *
obfuscateHeader: A list of header names that should be
+ * obfuscated in the logs. This list is provided during the instantiation of the
+ * filter.
+ *
+ *
+ *
Methods:
+ *
+ *
filter: The core method of the
+ * {@code WebClientLoggingFilter} class. It intercepts the request and response,
+ * logs request details, and then passes the request to the next
+ * {@link ExchangeFunction} in the chain. After receiving the response, it logs
+ * the response details before returning the {@link ClientResponse} to the
+ * caller.
+ *
+ *
Parameters:
+ *
+ *
request: The original {@link ClientRequest} object
+ * representing the HTTP request.
+ *
next: The next {@link ExchangeFunction} in the chain
+ * that will handle the request after this filter.
+ *
+ *
+ *
Returns: A {@link Mono} of {@link ClientResponse}
+ * representing the response returned by the next filter in the chain.
+ *
+ *
+ *
+ *
+ *
Logging:
+ *
+ *
Request Logging: Logs the HTTP method and URL of the
+ * request. If the request has a body, it is logged as well. Headers are logged
+ * in debug mode with sensitive values obfuscated if their names match any in
+ * the {@code obfuscateHeader} list.
+ *
Response Logging: Logs response headers in debug mode,
+ * with sensitive values obfuscated if necessary.
+ * The {@code WebClientLoggingFilter} class provides detailed logging for HTTP
+ * requests and responses. It is particularly useful for debugging and
+ * monitoring API interactions. The filter logs important request and response
+ * details, including headers and bodies (if present). Sensitive header values,
+ * such as authorization tokens or cookies, can be obfuscated in the logs by
+ * specifying their names in the {@code obfuscateHeader} list.
+ *
+ *
+ *
+ * Logging levels are controlled by the {@code log} object. Request and response
+ * bodies are logged if their length is greater than zero. Header names and
+ * values are logged at the debug level, with sensitive values replaced by a
+ * placeholder string.
+ *
+ *
+ *
+ * This filter can be added to a {@link} configuration to automatically apply
+ * logging to all requests made through that client.
+ *
+ */
@Slf4j
@RequiredArgsConstructor
public class WebClientLoggingFilter implements ExchangeFilterFunction {
diff --git a/src/main/java/io/hoangtien2k3/commons/filter/webclient/WebClientMonitoringFilter.java b/src/main/java/io/hoangtien2k3/commons/filter/webclient/WebClientMonitoringFilter.java
index 43b8c0f..0fb39a9 100644
--- a/src/main/java/io/hoangtien2k3/commons/filter/webclient/WebClientMonitoringFilter.java
+++ b/src/main/java/io/hoangtien2k3/commons/filter/webclient/WebClientMonitoringFilter.java
@@ -24,6 +24,107 @@
import org.springframework.web.reactive.function.client.ExchangeFunction;
import reactor.core.publisher.Mono;
+/**
+ * The `WebClientMonitoringFilter` class implements `ExchangeFilterFunction` to
+ * monitor and record metrics for HTTP requests made using Spring's `WebClient`.
+ * It utilizes Micrometer's `MeterRegistry` to collect and record metrics such
+ * as response time.
+ *
+ *
Attributes:
+ *
+ *
METRICS_WEBCLIENT_START_TIME: A constant string used as
+ * a key to store the start time of the HTTP request in the context.
+ *
meterRegistry: An instance of `MeterRegistry` used to
+ * register and publish metrics. It is provided during the instantiation of the
+ * filter.
+ *
tagsProvider: (Commented out) A
+ * `WebClientExchangeTagsProvider` for generating tags for the metrics. It is
+ * currently not used but can be configured for more detailed metrics.
+ *
+ *
+ *
Methods:
+ *
+ *
filter: This method is invoked for every HTTP request
+ * handled by the `WebClient`. It:
+ *
+ *
Records the start time of the request in the context.
+ *
Registers a `Timer` metric with `MeterRegistry` to measure the duration
+ * of the request and response.
+ *
Logs the request duration in seconds, including percentiles for response
+ * time.
+ *
+ *
+ *
Parameters:
+ *
+ *
clientRequest: The `ClientRequest` object representing
+ * the HTTP request.
+ *
exchangeFunction: The `ExchangeFunction` that processes
+ * the HTTP request and returns a `ClientResponse`.
+ *
+ *
+ *
Returns: A `Mono` representing the
+ * response from the exchange function.
+ *
+ *
+ *
+ *
+ *
Logging:
+ *
+ *
Monitoring Logging: Logs the duration of the API call in
+ * seconds. The duration is calculated based on the time elapsed between the
+ * start and end of the request.
+ * The `WebClientMonitoringFilter` class provides monitoring capabilities for
+ * HTTP requests made through Spring's `WebClient`. It is designed to track and
+ * record metrics such as response times to help in performance monitoring and
+ * analysis. The filter uses Micrometer's `MeterRegistry` to register and
+ * publish these metrics, allowing you to monitor the duration of HTTP requests.
+ *
+ *
+ *
+ * When a request is processed, the filter captures the start time and records
+ * the duration of the request once the response is received. The metrics are
+ * published with percentiles to provide insight into response time
+ * distributions. You can also log the duration of requests for further
+ * analysis.
+ *
+ *
+ *
+ * To use this filter, configure it as part of your `WebClient` setup and
+ * provide a `MeterRegistry` bean to register the metrics. This filter is useful
+ * in performance monitoring scenarios, where understanding request durations
+ * and response times is crucial for optimizing application performance.
+ *
+ */
@Slf4j
@Data
@RequiredArgsConstructor
diff --git a/src/main/java/io/hoangtien2k3/commons/filter/webclient/WebClientRetryHandler.java b/src/main/java/io/hoangtien2k3/commons/filter/webclient/WebClientRetryHandler.java
index 1e33cfe..caa6d6e 100644
--- a/src/main/java/io/hoangtien2k3/commons/filter/webclient/WebClientRetryHandler.java
+++ b/src/main/java/io/hoangtien2k3/commons/filter/webclient/WebClientRetryHandler.java
@@ -25,6 +25,111 @@
import reactor.core.publisher.Mono;
import reactor.util.retry.Retry;
+/**
+ * The `WebClientRetryHandler` class implements `ExchangeFilterFunction` to
+ * provide retry functionality for HTTP requests made using Spring's
+ * `WebClient`. It uses a configurable retry strategy to handle transient
+ * failures and retry requests based on specific conditions.
+ *
+ *
Attributes:
+ *
+ *
properties: An instance of `RetryProperties` used to
+ * configure retry behavior, such as the number of retry attempts, HTTP methods
+ * to apply retries, and exception types that should trigger retries.
+ *
+ *
+ *
Methods:
+ *
+ *
filter: This method is invoked for every HTTP request
+ * handled by the `WebClient`. It:
+ *
+ *
Creates a `Retry` instance with the configured properties, including the
+ * maximum number of retry attempts and the conditions under which retries
+ * should be performed.
+ *
Logs a warning message each time a retry is attempted, including the
+ * number of retries and the cause of the failure.
+ *
Throws an exception if all retry attempts are exhausted.
+ *
+ *
+ *
Parameters:
+ *
+ *
request: The `ClientRequest` object representing the
+ * HTTP request.
+ *
next: The `ExchangeFunction` that processes the HTTP
+ * request and returns a `ClientResponse`.
+ *
+ *
+ *
Returns: A `Mono` representing the
+ * response from the exchange function, which includes retry handling.
+ *
+ *
+ *
+ *
+ *
Logging:
+ *
+ *
Retry Logging: Logs a warning message each time a retry
+ * is performed, including the retry count and the cause of the failure. This
+ * helps in monitoring and debugging retry attempts.
+ * The `WebClientRetryHandler` class provides a way to handle transient errors
+ * and retry HTTP requests using Spring's `WebClient`. It allows you to
+ * configure retry behavior based on the number of retry attempts, the HTTP
+ * methods that should be retried, and the types of exceptions that should
+ * trigger retries. The retry logic is built using Reactor's retry
+ * functionality, which allows for configurable retry policies and handling of
+ * transient failures.
+ *
+ *
+ *
+ * When a request fails, the filter will attempt to retry the request based on
+ * the configured properties. The retry attempts are logged for monitoring
+ * purposes, and an exception is thrown if all retry attempts are exhausted.
+ * This functionality is useful in scenarios where transient errors are expected
+ * and retrying the request can lead to successful outcomes.
+ *
+ *
+ *
+ * To use this filter, configure it as part of your `WebClient` setup and
+ * provide a `RetryProperties` bean to specify the retry behavior. This filter
+ * is helpful for ensuring reliability in HTTP communications by automatically
+ * retrying requests in the event of transient failures.
+ *
+ */
@Slf4j
@RequiredArgsConstructor
public class WebClientRetryHandler implements ExchangeFilterFunction {
diff --git a/src/main/java/io/hoangtien2k3/commons/model/SagaProcess.java b/src/main/java/io/hoangtien2k3/commons/model/SagaProcess.java
index dd2413c..bdb2a29 100644
--- a/src/main/java/io/hoangtien2k3/commons/model/SagaProcess.java
+++ b/src/main/java/io/hoangtien2k3/commons/model/SagaProcess.java
@@ -24,13 +24,123 @@
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
+/**
+ * The `SagaProcess` class represents an abstract base class for implementing a
+ * saga pattern. It defines the structure and behavior of a saga process, which
+ * consists of a series of steps that need to be executed and potentially rolled
+ * back in case of errors.
+ *
+ *
Attributes:
+ *
+ *
executedStep: A `List` of `SagaStep` objects that keeps
+ * track of the steps that have been executed so far. It is used for rollback
+ * purposes in case of failures.
+ *
+ *
+ *
Abstract Methods:
+ *
+ *
getSteps(): This abstract method should be implemented
+ * by subclasses to return the list of `SagaStep` objects that define the steps
+ * of the saga process.
+ *
+ *
+ *
Methods:
+ *
+ *
execute(): Executes the steps of the saga process. It
+ * processes each step in sequence and handles the results. If any step fails,
+ * the process triggers a rollback of the previously executed steps. The method:
+ *
+ *
Logs the start of the execution.
+ *
Uses `Flux.fromIterable(getSteps())` to create a stream of steps.
+ *
Applies `flatMap(SagaStep::execute)` to execute each step
+ * asynchronously.
+ *
Handles the result of each step using `handle()` to either continue or
+ * trigger an error.
+ *
Subscribes on a bounded elastic scheduler to ensure that the operations
+ * are performed in a dedicated thread pool.
+ *
On error, calls the `revert()` method to roll back the changes made by
+ * the previously executed steps.
+ *
+ *
+ *
revert(): Rolls back the previously executed steps in
+ * reverse order. It is called when an error occurs during the execution of the
+ * saga process. The method:
+ *
+ *
Logs the start of the rollback.
+ *
Reverses the order of the steps using
+ * `Collections.reverse(getSteps())`.
+ *
Filters out the steps that have not been completed successfully and
+ * applies `flatMap(SagaStep::revert)` to revert each step asynchronously.
+ *
+ *
+ *
+ *
+ *
Usage Example:
+ *
+ *
{@code
+ * public class MySagaProcess extends SagaProcess {
+ * @Override
+ * public List getSteps() {
+ * return List.of(new Step1(), new Step2(), new Step3());
+ * }
+ * }
+ *
+ * // Create an instance of MySagaProcess
+ * SagaProcess sagaProcess = new MySagaProcess();
+ *
+ * // Execute the saga process
+ * sagaProcess.execute().doOnSuccess(result -> System.out.println("Saga executed successfully"))
+ * .doOnError(ex -> System.out.println("Saga execution failed: " + ex.getMessage())).subscribe();
+ * }
+ *
+ *
Detailed Description:
+ *
+ *
+ * The `SagaProcess` class is designed to manage complex transactional processes
+ * using the saga pattern. It provides a way to execute a series of steps while
+ * ensuring that if any step fails, the previously executed steps are rolled
+ * back to maintain consistency. The class uses reactive programming constructs
+ * from Project Reactor to handle asynchronous execution and rollback.
+ *
+ *
+ *
+ * Subclasses must implement the `getSteps()` method to provide the specific
+ * steps of the saga process. The `execute()` method orchestrates the execution
+ * of these steps and manages rollback in case of errors. The `revert()` method
+ * ensures that any changes made by the executed steps are undone in reverse
+ * order.
+ *
+ *
+ *
+ * This class is useful in scenarios where long-running transactions or complex
+ * business processes need to be managed, and where rollback capabilities are
+ * required to handle failures gracefully.
+ *
+ */
@Slf4j
public abstract class SagaProcess {
+ /**
+ * Abstract method that should be implemented by subclasses to return the list
+ * of `SagaStep` objects that define the steps of the saga process.
+ *
+ * @return A list of `SagaStep` objects.
+ */
public abstract List getSteps();
+ /**
+ * A `List` of `SagaStep` objects that keeps track of the steps that have been
+ * executed so far. This is used for rollback purposes in case of failures.
+ */
protected final List executedStep = new LinkedList<>();
+ /**
+ * Executes the steps of the saga process. It processes each step in sequence
+ * and handles the results. If any step fails, the process triggers a rollback
+ * of the previously executed steps.
+ *
+ * @return A `Flux` indicating the result of the saga execution.
+ */
public Flux> execute() {
log.info("==================Start execute================");
return Flux.fromIterable(getSteps())
@@ -47,6 +157,12 @@ public Flux> execute() {
.onErrorResume(ex -> revert().then(Mono.error(ex)));
}
+ /**
+ * Rolls back the previously executed steps in reverse order. It is called when
+ * an error occurs during the execution of the saga process.
+ *
+ * @return A `Flux` indicating the result of the rollback.
+ */
public Flux> revert() {
log.info("==================Start rollback================");
Collections.reverse(getSteps());
diff --git a/src/main/java/io/hoangtien2k3/commons/model/SagaStep.java b/src/main/java/io/hoangtien2k3/commons/model/SagaStep.java
index 77c177f..2faabc9 100644
--- a/src/main/java/io/hoangtien2k3/commons/model/SagaStep.java
+++ b/src/main/java/io/hoangtien2k3/commons/model/SagaStep.java
@@ -16,11 +16,130 @@
import reactor.core.publisher.Mono;
+/**
+ * The {@code SagaStep} interface defines a single step in a saga process. Each
+ * step represents an individual operation that can be executed, and, if
+ * necessary, rolled back in case of failure. The interface provides methods for
+ * executing, reverting, and checking the completion status of the step.
+ *
+ *
Methods:
+ *
+ *
complete(): Checks whether the step has been completed
+ * successfully. This is used in the rollback process to determine which steps
+ * need to be reverted.
+ *
execute(): Executes the step and returns a
+ * {@link Mono}<{@link StepResult}> indicating the result of the
+ * execution. This method is used in the saga process to perform the step's
+ * operation.
+ *
revert(): Reverts the step's changes if the execution
+ * fails. It returns a {@link Mono}<{@link Boolean}> indicating whether
+ * the revert operation was successful.
+ *
+ *
+ *
Methods Description:
+ *
+ *
boolean complete()
+ *
+ * This method is used to check whether the step has been completed
+ * successfully. It returns {@code true} if the step is complete, and
+ * {@code false} otherwise. This information is crucial for determining which
+ * steps need to be rolled back in case of an error.
+ *
+ *
+ *
Mono<StepResult> execute()
+ *
+ * This method is responsible for executing the step's operation. It returns a
+ * {@link Mono}<{@link StepResult}>, where {@link StepResult} encapsulates
+ * the outcome of the execution. The {@link StepResult} typically contains
+ * information about whether the execution was successful and any relevant
+ * messages. This method is called during the saga process to perform the step's
+ * operation.
+ *
+ *
+ *
Mono<Boolean> revert()
+ *
+ * This method is used to revert the step's changes if the execution fails. It
+ * returns a {@link Mono}<{@link Boolean}>, where {@code true} indicates
+ * that the revert operation was successful and {@code false} otherwise. This
+ * method is called during the rollback process to undo the changes made by the
+ * step if necessary.
+ *
+ *
+ *
Usage Example:
+ *
+ *
+ * {
+ * @code
+ * public class MySagaStep implements SagaStep {
+ *
+ * @Override
+ * public boolean complete() {
+ * // Check if the step has been completed
+ * return true; // or false based on the actual completion status
+ * }
+ *
+ * @Override
+ * public Mono execute() {
+ * // Perform the step's operation and return the result
+ * return Mono.just(new StepResult(true, "Operation succeeded"));
+ * }
+ *
+ * @Override
+ * public Mono revert() {
+ * // Revert the step's changes and return the result
+ * return Mono.just(true); // or false based on the actual revert outcome
+ * }
+ * }
+ * // Create an instance of MySagaStep
+ * SagaStep sagaStep = new MySagaStep();
+ * // Execute the step
+ * sagaStep.execute().doOnNext(result -> System.out.println("Step executed successfully: " + result))
+ * .doOnError(ex -> System.out.println("Step execution failed: " + ex.getMessage())).subscribe();
+ * // Revert the step
+ * sagaStep.revert().doOnNext(success -> System.out.println("Step reverted successfully: " + success))
+ * .doOnError(ex -> System.out.println("Step revert failed: " + ex.getMessage())).subscribe();
+ * }
+ *
+ *
+ *
Detailed Description:
+ *
+ *
+ * The {@code SagaStep} interface is a fundamental component in implementing the
+ * saga pattern. It defines the operations that can be performed as part of a
+ * saga process, including executing the step, checking its completion status,
+ * and reverting its changes if needed. Each step in the saga must implement
+ * this interface to be integrated into the saga process.
+ *
+ *
+ *
+ * By implementing the {@code SagaStep} interface, you can define specific
+ * behaviors for each step in your saga, handle successful execution, and manage
+ * rollback scenarios. This interface provides a structured way to manage
+ * complex transactional processes and ensure consistency in case of failures.
+ *
+ */
public interface SagaStep {
+ /**
+ * Checks whether the step has been completed successfully.
+ *
+ * @return {@code true} if the step is complete, {@code false} otherwise.
+ */
boolean complete();
+ /**
+ * Executes the step's operation and returns a {@link Mono} containing the
+ * result.
+ *
+ * @return A {@link Mono} containing the outcome of the execution.
+ */
Mono execute();
+ /**
+ * Reverts the step's changes if the execution fails.
+ *
+ * @return A {@link Mono}<{@link Boolean}> indicating whether the revert
+ * operation was successful.
+ */
Mono revert();
}
diff --git a/src/main/java/io/hoangtien2k3/commons/model/StepResult.java b/src/main/java/io/hoangtien2k3/commons/model/StepResult.java
index a8e7844..aec234a 100644
--- a/src/main/java/io/hoangtien2k3/commons/model/StepResult.java
+++ b/src/main/java/io/hoangtien2k3/commons/model/StepResult.java
@@ -16,20 +16,102 @@
import lombok.Getter;
+/**
+ * The `StepResult` class represents the result of executing a step in a saga
+ * process. It encapsulates the outcome of the step execution, including whether
+ * it was successful and any associated message.
+ *
+ *
Fields:
+ *
+ *
success: A boolean indicating whether the step execution
+ * was successful. `true` if the execution was successful, `false`
+ * otherwise.
+ *
message: A string containing any message associated with
+ * the result. This is typically used to provide additional information or error
+ * messages when the step fails.
+ *
+ *
+ *
Constructors:
+ *
+ * The constructor is private and only accessible through the static factory
+ * methods. This design ensures that instances of `StepResult` are created with
+ * specific success and failure states.
+ *
+ *
+ *
Static Methods:
+ *
+ *
success(): Creates a `StepResult` instance representing
+ * a successful execution. The message will be `null`.
+ *
failure(String message): Creates a `StepResult` instance
+ * representing a failed execution. The provided message will be used to
+ * describe the failure.
+ *
+ *
+ *
Usage Example:
+ *
+ *
{@code
+ * // Creating a successful result
+ * StepResult successResult = StepResult.success();
+ *
+ * // Creating a failure result with a message
+ * StepResult failureResult = StepResult.failure("An error occurred while executing the step.");
+ *
+ * // Checking the result
+ * if (successResult.isSuccess()) {
+ * System.out.println("Step executed successfully.");
+ * } else {
+ * System.out.println("Step failed: " + failureResult.getMessage());
+ * }
+ * }
+ *
+ *
Detailed Description:
+ *
+ *
+ * The `StepResult` class is used to encapsulate the result of executing a step
+ * in a saga process. It provides a clear and structured way to represent
+ * whether the execution was successful or failed, along with an optional
+ * message that describes the outcome. This is useful for handling success and
+ * failure scenarios in the saga process and ensuring that appropriate actions
+ * are taken based on the result of each step.
+ *
+ */
@Getter
public class StepResult {
private final boolean success;
private final String message;
+ /**
+ * Private constructor to create an instance of `StepResult`.
+ *
+ * @param success
+ * Indicates whether the step execution was successful.
+ * @param message
+ * A message associated with the result, or `null` if there is no
+ * message.
+ */
private StepResult(boolean success, String message) {
this.success = success;
this.message = message;
}
+ /**
+ * Creates a `StepResult` instance representing a successful execution.
+ *
+ * @return A `StepResult` instance with `success` set to `true` and `message`
+ * set to `null`.
+ */
public static StepResult success() {
return new StepResult(true, null);
}
+ /**
+ * Creates a `StepResult` instance representing a failed execution.
+ *
+ * @param message
+ * A message describing the failure.
+ * @return A `StepResult` instance with `success` set to `false` and the
+ * provided `message`.
+ */
public static StepResult failure(String message) {
return new StepResult(false, message);
}
diff --git a/src/main/java/io/hoangtien2k3/commons/model/TokenUser.java b/src/main/java/io/hoangtien2k3/commons/model/TokenUser.java
index 42370d5..aef2dbe 100644
--- a/src/main/java/io/hoangtien2k3/commons/model/TokenUser.java
+++ b/src/main/java/io/hoangtien2k3/commons/model/TokenUser.java
@@ -21,6 +21,75 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * The `TokenUser` class represents a user token with various properties related
+ * to user identity and organization. It is used to encapsulate user information
+ * typically obtained from an authentication system or token service.
+ *
+ *
Annotations:
+ *
+ *
@JsonIgnoreProperties: Specifies that any properties not
+ * bound to this instance are ignored during deserialization. This helps handle
+ * cases where the JSON data contains extra properties not defined in the
+ * `TokenUser` class.
+ *
@Data: Lombok annotation that generates getters,
+ * setters, equals, hashCode, and toString methods, as well as a constructor
+ * with all arguments and a no-args constructor.
+ *
@AllArgsConstructor: Lombok annotation that generates a
+ * constructor with all fields as arguments.
+ *
@NoArgsConstructor: Lombok annotation that generates a
+ * no-arguments constructor.
+ *
@Builder: Lombok annotation that generates a builder
+ * pattern for the class, allowing fluent and immutable object creation.
+ *
+ *
+ *
Fields:
+ *
+ *
id: A unique identifier for the user.
+ *
name: The full name of the user.
+ *
username: The username of the user, typically used for
+ * login.
+ *
email: The email address of the user.
+ *
individualId: An identifier for the individual
+ * associated with the user. Annotated with @JsonProperty("individual_id") to
+ * map the JSON property "individual_id" to this field.
+ *
organizationId: An identifier for the organization
+ * associated with the user.
+ * The `TokenUser` class serves as a data model for holding user information
+ * extracted from authentication tokens or identity services. It includes fields
+ * for user identification, personal details, and organizational information.
+ * The class is annotated with Lombok annotations to simplify the creation and
+ * manipulation of `TokenUser` instances.
+ *
+ *
+ *
+ * With the `@JsonIgnoreProperties` annotation, the class can handle extra
+ * properties in JSON data gracefully. The `@JsonProperty("individual_id")`
+ * annotation ensures that the JSON field "individual_id" is correctly mapped to
+ * the `individualId` field in the class.
+ *
+ */
@JsonIgnoreProperties
@Data
@AllArgsConstructor
diff --git a/src/main/java/io/hoangtien2k3/commons/model/UserDTO.java b/src/main/java/io/hoangtien2k3/commons/model/UserDTO.java
index 3aa5980..3f4d5a9 100644
--- a/src/main/java/io/hoangtien2k3/commons/model/UserDTO.java
+++ b/src/main/java/io/hoangtien2k3/commons/model/UserDTO.java
@@ -19,6 +19,69 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * The `UserDTO` class represents a Data Transfer Object (DTO) used for
+ * user-related information. It is typically used to encapsulate user data
+ * obtained from authentication or authorization services.
+ *
+ *
Annotations:
+ *
+ *
@Data: Lombok annotation that generates getters,
+ * setters, equals, hashCode, and toString methods, as well as a constructor
+ * with all arguments and a no-args constructor.
+ *
@NoArgsConstructor: Lombok annotation that generates a
+ * no-arguments constructor.
+ *
@AllArgsConstructor: Lombok annotation that generates a
+ * constructor with all fields as arguments.
+ *
@JsonProperty: Jackson annotation used to map JSON
+ * properties to Java fields, ensuring the correct mapping between JSON data and
+ * the DTO fields.
+ *
+ *
+ *
Fields:
+ *
+ *
id: The unique identifier of the user, mapped from the
+ * JSON property "sub".
+ *
username: The preferred username of the user, mapped
+ * from the JSON property "preferred_username".
+ *
+ *
+ *
Usage Example:
+ *
+ *
{@code
+ * // Creating a UserDTO instance using the all-args constructor
+ * UserDTO user = new UserDTO("12345", "tien");
+ *
+ * // Accessing properties
+ * String userId = user.getId();
+ * String username = user.getUsername();
+ *
+ * // Displaying user information
+ * System.out.println("User ID: " + user.getId());
+ * System.out.println("Username: " + user.getUsername());
+ *
+ * // Creating a UserDTO instance using the no-args constructor and setters
+ * UserDTO anotherUser = new UserDTO();
+ * anotherUser.setId("67890");
+ * anotherUser.setUsername("hoangtien2k3");
+ * }
+ *
+ *
Detailed Description:
+ *
+ *
+ * The `UserDTO` class is a simple data structure used to transfer user
+ * information between different layers of an application or between systems. It
+ * includes fields for a unique user ID and a preferred username.
+ *
+ *
+ *
+ * The class is annotated with Lombok annotations to simplify the creation and
+ * manipulation of `UserDTO` instances. The `@JsonProperty` annotations are used
+ * to ensure that JSON properties are correctly mapped to the Java fields, which
+ * is particularly useful when working with JSON data from external services or
+ * APIs.
+ *
+ */
@Data
@NoArgsConstructor
@AllArgsConstructor
diff --git a/src/main/java/io/hoangtien2k3/commons/model/WhiteList.java b/src/main/java/io/hoangtien2k3/commons/model/WhiteList.java
index f00c42a..f3845cd 100644
--- a/src/main/java/io/hoangtien2k3/commons/model/WhiteList.java
+++ b/src/main/java/io/hoangtien2k3/commons/model/WhiteList.java
@@ -19,6 +19,60 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * The `WhiteList` class represents the configuration for public APIs that do
+ * not require authentication. This class is used to define which URIs and HTTP
+ * methods are allowed to be accessed without authentication.
+ *
+ *
Annotations:
+ *
+ *
@Data: Lombok annotation that automatically generates
+ * getters, setters, equals, hashCode, and toString methods, along with a
+ * constructor with all arguments and a no-arguments constructor.
+ *
@NoArgsConstructor: Lombok annotation that generates a
+ * no-arguments constructor.
+ *
@AllArgsConstructor: Lombok annotation that generates a
+ * constructor with all fields as arguments.
+ *
+ *
+ *
Fields:
+ *
+ *
uri: The URI pattern that is whitelisted and accessible
+ * without authentication. For example, "/public/api" or "/v1/resources".
+ *
methods: A list of HTTP methods (e.g., GET, POST, PUT)
+ * that are allowed for the given URI. This allows specifying which HTTP methods
+ * are permitted for the URI. If the list is empty, all methods are allowed for
+ * the URI.
+ *
+ *
+ *
Configuration Example:
+ *
+ *
{@code
+ * # In application.yml
+ * whitelist:
+ * - uri: /public/api
+ * methods:
+ * - GET
+ * - POST
+ * - uri: /v1/resources
+ * methods:
+ * - GET
+ * }
+ *
+ *
Usage:
+ *
+ * The `WhiteList` class is used in the configuration of your application to
+ * define which endpoints can be accessed without authentication. This
+ * configuration helps in making certain APIs publicly accessible while securing
+ * other parts of your application.
+ *
+ *
+ *
+ * The class is typically used in conjunction with Spring Boot's configuration
+ * properties support, allowing you to easily inject these configurations into
+ * your application's security setup.
+ *
+ */
@Data
@AllArgsConstructor
@NoArgsConstructor
diff --git a/src/main/java/io/hoangtien2k3/commons/model/logging/HttpLogRequest.java b/src/main/java/io/hoangtien2k3/commons/model/logging/HttpLogRequest.java
index 3ff4e4c..eebd448 100644
--- a/src/main/java/io/hoangtien2k3/commons/model/logging/HttpLogRequest.java
+++ b/src/main/java/io/hoangtien2k3/commons/model/logging/HttpLogRequest.java
@@ -18,6 +18,68 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * The `HttpLogRequest` class is a configuration class used to control whether
+ * HTTP request logging is enabled or disabled.
+ *
+ *
Attributes:
+ *
+ *
enable: A boolean flag indicating whether HTTP request
+ * logging is enabled. Default value is true.
+ *
+ *
+ *
Constructor Details:
+ *
+ *
HttpLogRequest(): Default constructor that initializes
+ * enable to true.
+ *
HttpLogRequest(boolean enable): Constructor that allows
+ * setting the enable flag explicitly.
+ * The `HttpLogRequest` class provides a simple way to configure logging of HTTP
+ * requests within an application. By setting the enable attribute
+ * to true, you can enable detailed logging for HTTP requests. If
+ * set to false, logging is disabled, which can be useful for
+ * reducing log verbosity in production environments or when detailed request
+ * logging is not required.
+ *
+ *
+ *
+ * This class can be used in conjunction with other logging configuration
+ * classes or aspects to conditionally enable or disable HTTP request logging
+ * based on the application's needs. It helps in centralizing the configuration
+ * of logging behavior and makes it easier to manage and control logging
+ * settings.
+ *
+ */
@Data
@AllArgsConstructor
@NoArgsConstructor
diff --git a/src/main/java/io/hoangtien2k3/commons/model/logging/HttpLogResponse.java b/src/main/java/io/hoangtien2k3/commons/model/logging/HttpLogResponse.java
index 0e06dd3..ed09dff 100644
--- a/src/main/java/io/hoangtien2k3/commons/model/logging/HttpLogResponse.java
+++ b/src/main/java/io/hoangtien2k3/commons/model/logging/HttpLogResponse.java
@@ -18,6 +18,68 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * The {@code HttpLogResponse} class is a configuration class used to manage
+ * whether HTTP response logging is enabled or disabled.
+ *
+ *
Attributes:
+ *
+ *
enable: A boolean flag indicating whether HTTP response
+ * logging is enabled. Default value is {@code true}.
+ *
+ *
+ *
Constructor Details:
+ *
+ *
HttpLogResponse(): Default constructor that initializes
+ * {@code enable} to {@code true}.
+ *
HttpLogResponse(boolean enable): Constructor that allows
+ * setting the {@code enable} flag explicitly.
+ * The {@code HttpLogResponse} class provides a mechanism to configure logging
+ * of HTTP responses within an application. By setting the {@code enable}
+ * attribute to {@code true}, detailed logging of HTTP responses is enabled.
+ * Setting it to {@code false} disables response logging, which can be useful to
+ * reduce log verbosity in production environments or when response logging is
+ * not necessary.
+ *
+ *
+ *
+ * This class can be used in combination with other logging configuration
+ * classes to control the logging behavior for HTTP responses. It centralizes
+ * the logging configuration and simplifies the management of logging settings
+ * for HTTP responses.
+ *
+ */
@Data
@AllArgsConstructor
@NoArgsConstructor
diff --git a/src/main/java/io/hoangtien2k3/commons/model/logging/LogField.java b/src/main/java/io/hoangtien2k3/commons/model/logging/LogField.java
index 8441711..07bbf73 100644
--- a/src/main/java/io/hoangtien2k3/commons/model/logging/LogField.java
+++ b/src/main/java/io/hoangtien2k3/commons/model/logging/LogField.java
@@ -16,6 +16,88 @@
import lombok.*;
+/**
+ * The `LogField` class is a data transfer object (DTO) designed to encapsulate
+ * various fields of logging information that can be used for structured logging
+ * in an application. This class is used to represent and store log data with
+ * detailed information about requests and responses.
+ *
+ *
Attributes:
+ *
+ *
traceId: A unique identifier for tracking the trace of a
+ * request across different services.
+ *
requestId: A unique identifier for the specific request,
+ * often used to correlate logs for a single request.
+ *
service: The name of the service that generated the log
+ * entry.
+ *
duration: The duration of the request or operation in
+ * milliseconds.
+ *
logType: The type of log entry (e.g., INFO, ERROR,
+ * DEBUG).
+ *
actionType: The type of action or operation being logged
+ * (e.g., CREATE, UPDATE, DELETE).
+ *
startTime: The start time of the operation in
+ * milliseconds since epoch.
+ *
endTime: The end time of the operation in milliseconds
+ * since epoch.
+ *
clientAddress: The IP address of the client making the
+ * request.
+ *
title: A brief title or description of the log
+ * entry.
+ *
inputs: The input data or parameters provided in the
+ * request.
+ *
response: The response data or result returned by the
+ * service.
+ *
result: The outcome or result of the operation (e.g.,
+ * SUCCESS, FAILURE).
+ *
+ *
+ *
Constructors:
+ *
+ *
LogField(): Default constructor.
+ *
LogField(String traceId, String requestId, String service, Long
+ * duration, String logType, String actionType, Long startTime, Long endTime,
+ * String clientAddress, String title, String inputs, String response, String
+ * result): Constructor with parameters to initialize all
+ * attributes.
+ *
+ *
+ *
Methods:
+ *
+ *
Getters and Setters: Methods to get and set the values
+ * of each attribute.
+ *
Builder: Provides a builder pattern for creating
+ * instances of `LogField` with a fluent API.
+ * The `LogField` class is used to structure and manage log data in a consistent
+ * manner. It includes fields for capturing detailed information about each log
+ * entry, such as trace IDs, request IDs, service names, durations, log types,
+ * and more. This structured approach helps in analyzing logs more effectively
+ * and correlating log entries across different components and services.
+ *
+ *
+ *
+ * By using the builder pattern, the `LogField` class provides a flexible and
+ * convenient way to create instances with various combinations of attributes.
+ * The class can be integrated with logging frameworks to enhance the quality
+ * and usability of log data.
+ *
+ */
@Data
@NoArgsConstructor
@AllArgsConstructor
diff --git a/src/main/java/io/hoangtien2k3/commons/model/logging/LoggerDTO.java b/src/main/java/io/hoangtien2k3/commons/model/logging/LoggerDTO.java
index 3c50d07..11cafbd 100644
--- a/src/main/java/io/hoangtien2k3/commons/model/logging/LoggerDTO.java
+++ b/src/main/java/io/hoangtien2k3/commons/model/logging/LoggerDTO.java
@@ -21,19 +21,155 @@
import lombok.NoArgsConstructor;
import reactor.util.context.Context;
+/**
+ * The `LoggerDTO` class represents a Data Transfer Object (DTO) designed for
+ * logging purposes. It encapsulates various fields related to logging
+ * information, including context, span details, timing, results, and more. This
+ * class is used to store and manage logging data for more structured and
+ * detailed log entries.
+ *
+ *
Attributes:
+ *
+ *
contextRef: An `AtomicReference` containing the current
+ * `Context`. This is used to manage and share logging context information
+ * safely across threads.
+ *
newSpan: A `Span` object representing a new span in a
+ * distributed tracing system. This is useful for tracking the duration and
+ * details of specific operations in the system.
+ *
service: A `String` representing the name of the service
+ * generating the log entry.
+ *
startTime: A `Long` representing the start time of the
+ * operation in milliseconds since epoch.
+ *
endTime: A `Long` representing the end time of the
+ * operation in milliseconds since epoch.
+ *
result: A `String` indicating the result of the
+ * operation (e.g., SUCCESS, FAILURE).
+ *
response: An `Object` holding the response data returned
+ * by the service or operation.
+ *
logType: A `String` specifying the type of log entry
+ * (e.g., INFO, ERROR, DEBUG).
+ *
actionType: A `String` describing the type of action or
+ * operation being logged (e.g., CREATE, UPDATE, DELETE).
+ *
args: An array of `Object` holding the arguments or
+ * parameters passed to the operation.
+ *
title: A `String` providing a brief title or description
+ * of the log entry.
+ *
+ *
+ *
Constructors:
+ *
+ *
LoggerDTO(): Default constructor that initializes an
+ * empty instance.
+ *
LoggerDTO(AtomicReference<Context> contextRef, Span
+ * newSpan, String service, Long startTime, Long endTime, String result, Object
+ * response, String logType, String actionType, Object[] args, String
+ * title): Constructor that initializes all attributes with provided
+ * values.
+ *
+ *
+ *
Methods:
+ *
+ *
Getters and Setters: Methods to access and modify the
+ * values of each attribute.
+ *
+ *
+ *
Usage Example:
+ *
+ *
{@code
+ * // Create a new LoggerDTO instance with sample data
+ * LoggerDTO loggerDTO = new LoggerDTO(new AtomicReference<>(Context.current()), // Current context
+ * Span.current(), // Current span
+ * "UserService", // Service name
+ * System.currentTimeMillis() - 100, // Start time
+ * System.currentTimeMillis(), // End time
+ * "SUCCESS", // Result of the operation
+ * "{ \"status\": \"ok\" }", // Response data
+ * "INFO", // Log type
+ * "CREATE", // Action type
+ * new Object[]{"param1", "param2"}, // Arguments
+ * "User Created" // Title
+ * );
+ *
+ * // Use the LoggerDTO instance for logging
+ * log.info("Logging entry: {}", loggerDTO);
+ * }
+ *
+ *
Detailed Description:
+ *
+ *
+ * The `LoggerDTO` class is designed to hold comprehensive details related to
+ * logging in an application. It captures information such as context, span,
+ * service name, timing, results, responses, and more. This structured approach
+ * helps in generating detailed and organized log entries, making it easier to
+ * monitor and troubleshoot the application.
+ *
+ *
+ *
+ * By using `AtomicReference` for context management and `Span` for distributed
+ * tracing, the class supports advanced logging scenarios in multi-threaded and
+ * distributed environments. The `LoggerDTO` class is useful for creating
+ * detailed log entries with all necessary context and metadata, enhancing the
+ * quality and usefulness of logs.
+ *
+ */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoggerDTO {
- AtomicReference contextRef;
- Span newSpan;
- String service;
- Long startTime;
- Long endTime;
- String result;
- Object response;
- String logType;
- String actionType;
- Object[] args;
- String title;
+ /**
+ * An `AtomicReference` containing the current `Context`, used for managing and
+ * sharing logging context information safely across threads.
+ */
+ private AtomicReference contextRef;
+
+ /**
+ * A `Span` object representing a new span in a distributed tracing system,
+ * useful for tracking the duration and details of specific operations.
+ */
+ private Span newSpan;
+
+ /**
+ * The name of the service generating the log entry.
+ */
+ private String service;
+
+ /**
+ * The start time of the operation in milliseconds since epoch.
+ */
+ private Long startTime;
+
+ /**
+ * The end time of the operation in milliseconds since epoch.
+ */
+ private Long endTime;
+
+ /**
+ * The result of the operation (e.g., SUCCESS, FAILURE).
+ */
+ private String result;
+
+ /**
+ * The response data returned by the service or operation.
+ */
+ private Object response;
+
+ /**
+ * The type of log entry (e.g., INFO, ERROR, DEBUG).
+ */
+ private String logType;
+
+ /**
+ * The type of action or operation being logged (e.g., CREATE, UPDATE, DELETE).
+ */
+ private String actionType;
+
+ /**
+ * An array of arguments or parameters passed to the operation.
+ */
+ private Object[] args;
+
+ /**
+ * A brief title or description of the log entry.
+ */
+ private String title;
}
diff --git a/src/main/java/io/hoangtien2k3/commons/utils/MessageUtils.java b/src/main/java/io/hoangtien2k3/commons/utils/MessageUtils.java
index b016bae..4028521 100644
--- a/src/main/java/io/hoangtien2k3/commons/utils/MessageUtils.java
+++ b/src/main/java/io/hoangtien2k3/commons/utils/MessageUtils.java
@@ -20,15 +20,170 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.i18n.LocaleContextHolder;
+/**
+ * Utility class for handling internationalized messages.
+ *
+ * The {@code MessageUtils} class provides methods to retrieve localized
+ * messages from a resource bundle. It supports message formatting and fallback
+ * mechanisms for missing or erroneous messages.
+ *
+ *
Class Overview:
+ *
+ * The class uses {@code ResourceBundle} to fetch messages based on the provided
+ * locale. It formats the message using {@code MessageFormat} and handles
+ * exceptions to ensure that the application continues to function even if a
+ * message is missing or the resource bundle is misconfigured.
+ *
+ *
+ *
Fields:
+ *
+ *
BASE_NAME: The base name of the resource bundle, which
+ * is typically the name of the properties file (without the .properties
+ * extension) containing the messages. Defaults to "messages".
+ *
+ *
+ *
Methods:
+ *
+ *
getMessage(String code, Locale locale): Retrieves the
+ * message associated with the given code and locale. If the message is missing
+ * or an error occurs, the code itself is returned as a fallback.
+ *
+ * Parameters:
+ *
+ *
+ *
code: The key for the desired message.
+ *
locale: The locale to use for message retrieval.
+ *
+ *
+ * Returns:
+ *
+ *
+ *
The localized message, or the code itself if the message is not
+ * found.
+ *
+ *
+ *
+ *
getMessage(String code, Locale locale, Object... args):
+ * Retrieves and formats the message associated with the given code and locale,
+ * using the provided arguments.
+ *
+ * Parameters:
+ *
+ *
+ *
code: The key for the desired message.
+ *
locale: The locale to use for message retrieval.
+ *
args: Arguments to format the message.
+ *
+ *
+ * Returns:
+ *
+ *
+ *
The formatted message, or the code itself if the message is not
+ * found.
+ *
+ *
+ *
+ *
getMessage(String code): Retrieves the message
+ * associated with the given code, using the default locale from
+ * {@code LocaleContextHolder}.
+ *
+ * Parameters:
+ *
+ *
+ *
code: The key for the desired message.
+ *
+ *
+ * Returns:
+ *
+ *
+ *
The localized message, or the code itself if the message is not
+ * found.
+ *
+ *
+ *
+ *
getMessage(String code, Object... args): Retrieves and
+ * formats the message associated with the given code, using the default locale
+ * from {@code LocaleContextHolder} and the provided arguments.
+ *
+ * Parameters:
+ *
+ *
+ *
code: The key for the desired message.
+ *
args: Arguments to format the message.
+ *
+ *
+ * Returns:
+ *
+ *
+ *
The formatted message, or the code itself if the message is not
+ * found.
+ *
+ *
+ *
+ *
+ *
Usage Example:
+ *
+ *
{@code
+ * // Assuming there is a message property file named "messages.properties" with
+ * // a key "welcome.message"
+ * String message = MessageUtils.getMessage("welcome.message", Locale.US);
+ * // Output: "Welcome to our application!"
+ *
+ * // With arguments
+ * String formattedMessage = MessageUtils.getMessage("welcome.user", Locale.US, "John");
+ * // Output: "Welcome to our application, John!"
+ *
+ * // Using default locale
+ * String defaultMessage = MessageUtils.getMessage("default.message");
+ * // Output will be based on the default locale set in LocaleContextHolder
+ * }
+ *
+ *
Notes:
+ *
+ * The `MessageUtils` class relies on `ResourceBundle` for message retrieval.
+ * Ensure that the resource bundle files (e.g., `messages.properties`,
+ * `messages_en.properties`) are correctly placed in the classpath. The class
+ * also uses `LocaleContextHolder` to fetch the default locale, which should be
+ * properly configured in your Spring application context.
+ *
+ *
+ *
+ * Logging is performed for exceptions occurring during message retrieval,
+ * helping in debugging issues with missing or malformed messages.
+ *
+ */
@Slf4j
public class MessageUtils {
private static final String BASE_NAME = "messages";
+ /**
+ * Retrieves the message associated with the given code and locale.
+ *
+ * @param code
+ * The key for the desired message.
+ * @param locale
+ * The locale to use for message retrieval.
+ * @return The localized message, or the code itself if the message is not
+ * found.
+ */
public static String getMessage(String code, Locale locale) {
return getMessage(code, locale, null);
}
+ /**
+ * Retrieves and formats the message associated with the given code and locale,
+ * using the provided arguments.
+ *
+ * @param code
+ * The key for the desired message.
+ * @param locale
+ * The locale to use for message retrieval.
+ * @param args
+ * Arguments to format the message.
+ * @return The formatted message, or the code itself if the message is not
+ * found.
+ */
public static String getMessage(String code, Locale locale, Object... args) {
ResourceBundle resourceBundle = ResourceBundle.getBundle(BASE_NAME, locale);
String message;
@@ -43,10 +198,30 @@ public static String getMessage(String code, Locale locale, Object... args) {
return message;
}
+ /**
+ * Retrieves the message associated with the given code, using the default
+ * locale from LocaleContextHolder.
+ *
+ * @param code
+ * The key for the desired message.
+ * @return The localized message, or the code itself if the message is not
+ * found.
+ */
public static String getMessage(String code) {
return getMessage(code, LocaleContextHolder.getLocale(), null);
}
+ /**
+ * Retrieves and formats the message associated with the given code, using the
+ * default locale from LocaleContextHolder and the provided arguments.
+ *
+ * @param code
+ * The key for the desired message.
+ * @param args
+ * Arguments to format the message.
+ * @return The formatted message, or the code itself if the message is not
+ * found.
+ */
public static String getMessage(String code, Object... args) {
return getMessage(code, LocaleContextHolder.getLocale(), args);
}
diff --git a/src/main/java/io/hoangtien2k3/commons/utils/MinioUtils.java b/src/main/java/io/hoangtien2k3/commons/utils/MinioUtils.java
index f2d1290..52a716c 100644
--- a/src/main/java/io/hoangtien2k3/commons/utils/MinioUtils.java
+++ b/src/main/java/io/hoangtien2k3/commons/utils/MinioUtils.java
@@ -40,6 +40,138 @@
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
+/**
+ * Utility class for handling internationalized messages.
+ *
+ * The {@code MessageUtils} class provides methods to retrieve localized
+ * messages from a resource bundle. It supports message formatting and fallback
+ * mechanisms for missing or erroneous messages.
+ *
+ *
Class Overview:
+ *
+ * The class uses {@code ResourceBundle} to fetch messages based on the provided
+ * locale. It formats the message using {@code MessageFormat} and handles
+ * exceptions to ensure that the application continues to function even if a
+ * message is missing or the resource bundle is misconfigured.
+ *
+ *
+ *
Fields:
+ *
+ *
BASE_NAME: The base name of the resource bundle, which
+ * is typically the name of the properties file (without the .properties
+ * extension) containing the messages. Defaults to "messages".
+ *
+ *
+ *
Methods:
+ *
+ *
getMessage(String code, Locale locale): Retrieves the
+ * message associated with the given code and locale. If the message is missing
+ * or an error occurs, the code itself is returned as a fallback.
+ *
+ * Parameters:
+ *
+ *
+ *
code: The key for the desired message.
+ *
locale: The locale to use for message retrieval.
+ *
+ *
+ * Returns:
+ *
+ *
+ *
The localized message, or the code itself if the message is not
+ * found.
+ *
+ *
+ *
+ *
getMessage(String code, Locale locale, Object... args):
+ * Retrieves and formats the message associated with the given code and locale,
+ * using the provided arguments.
+ *
+ * Parameters:
+ *
+ *
+ *
code: The key for the desired message.
+ *
locale: The locale to use for message retrieval.
+ *
args: Arguments to format the message.
+ *
+ *
+ * Returns:
+ *
+ *
+ *
The formatted message, or the code itself if the message is not
+ * found.
+ *
+ *
+ *
+ *
getMessage(String code): Retrieves the message
+ * associated with the given code, using the default locale from
+ * {@code LocaleContextHolder}.
+ *
+ * Parameters:
+ *
+ *
+ *
code: The key for the desired message.
+ *
+ *
+ * Returns:
+ *
+ *
+ *
The localized message, or the code itself if the message is not
+ * found.
+ *
+ *
+ *
+ *
getMessage(String code, Object... args): Retrieves and
+ * formats the message associated with the given code, using the default locale
+ * from {@code LocaleContextHolder} and the provided arguments.
+ *
+ * Parameters:
+ *
+ *
+ *
code: The key for the desired message.
+ *
args: Arguments to format the message.
+ *
+ *
+ * Returns:
+ *
+ *
+ *
The formatted message, or the code itself if the message is not
+ * found.
+ *
+ *
+ *
+ *
+ *
Usage Example:
+ *
+ *
{@code
+ * // Assuming there is a message property file named "messages.properties" with
+ * // a key "welcome.message"
+ * String message = MessageUtils.getMessage("welcome.message", Locale.US);
+ * // Output: "Welcome to our application!"
+ *
+ * // With arguments
+ * String formattedMessage = MessageUtils.getMessage("welcome.user", Locale.US, "Tien");
+ * // Output: "Welcome to our application, John!"
+ *
+ * // Using default locale
+ * String defaultMessage = MessageUtils.getMessage("default.message");
+ * // Output will be based on the default locale set in LocaleContextHolder
+ * }
+ *
+ *
Notes:
+ *
+ * The `MessageUtils` class relies on `ResourceBundle` for message retrieval.
+ * Ensure that the resource bundle files (e.g., `messages.properties`,
+ * `messages_en.properties`) are correctly placed in the classpath. The class
+ * also uses `LocaleContextHolder` to fetch the default locale, which should be
+ * properly configured in your Spring application context.
+ *
+ *
+ *
+ * Logging is performed for exceptions occurring during message retrieval,
+ * helping in debugging issues with missing or malformed messages.
+ *
+ * The {@code PageUtils} class provides a static method to calculate the offset
+ * for pagination based on the current page number and page size. This is
+ * particularly useful for retrieving paginated results from a database or other
+ * data sources.
+ *
+ *
+ *
Class Overview:
+ *
+ * This class contains utility methods for handling pagination calculations.
+ * Currently, it includes a method to compute the offset given a page number and
+ * page size.
+ *
+ *
+ *
Methods:
+ *
+ *
getOffset: Calculates the offset for pagination based on
+ * the provided page number and page size.
+ *
+ *
Parameters:
+ *
+ *
page (Integer): The current page number. Must be greater
+ * than 0.
+ *
size (Integer): The number of items per page. Must be
+ * greater than 0.
+ *
+ *
+ *
Returns:
+ *
+ * An integer representing the offset. If the page or
+ * size is null or less than or equal to 0, the method returns 0.
+ *
+ *
+ *
Usage:
+ *
+ * This method can be used to calculate the starting index for a particular page
+ * in a paginated data set. For example, if the page size is 10 and the current
+ * page is 3, the method will calculate an offset of 20, which is the starting
+ * index for the 3rd page (page index starts at 0).
+ *
+ *
+ *
+ *
+ *
+ *
+ *
Usage Example:
+ *
+ *
{@code
+ * int page = 3;
+ * int size = 10;
+ * int offset = PageUtils.getOffset(page, size);
+ * // offset will be 20, which is used to fetch records starting from index 20
+ * }
+ *
+ *
Notes:
+ *
+ * Ensure that the page number and page size are validated before calling the
+ * method. If either value is invalid (e.g., less than or equal to 0), the
+ * method returns 0, which might result in incorrect pagination behavior if not
+ * handled properly in the application logic.
+ *
+ */
public class PageUtils {
+
+ /**
+ * Calculates the offset for pagination based on the current page number and
+ * page size.
+ *
+ * This method computes the starting index for the given page number and page
+ * size. The offset is calculated as (page - 1) * size. If the page or size is
+ * null or not positive, it returns 0.
+ *
+ *
+ * @param page
+ * The current page number. Must be greater than 0.
+ * @param size
+ * The number of items per page. Must be greater than 0.
+ * @return The calculated offset. Returns 0 if page or size is null or less than
+ * or equal to 0.
+ */
public static int getOffset(Integer page, Integer size) {
if (page == null || page <= 0 || size == null || size <= 0) return 0;
return (page - 1) * size;
diff --git a/src/main/java/io/hoangtien2k3/commons/utils/ReactiveOAuth2Utils.java b/src/main/java/io/hoangtien2k3/commons/utils/ReactiveOAuth2Utils.java
index 190fec8..45a9ca0 100644
--- a/src/main/java/io/hoangtien2k3/commons/utils/ReactiveOAuth2Utils.java
+++ b/src/main/java/io/hoangtien2k3/commons/utils/ReactiveOAuth2Utils.java
@@ -17,10 +17,100 @@
import org.springframework.security.oauth2.client.*;
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
+/**
+ * Utility class for creating a {@link ReactiveOAuth2AuthorizedClientManager}
+ * instance.
+ *
+ * The {@code ReactiveOAuth2Utils} class provides a static method to configure
+ * and instantiate a {@link ReactiveOAuth2AuthorizedClientManager} with a
+ * specific {@link ReactiveOAuth2AuthorizedClientProvider}. This is useful for
+ * managing OAuth2 clients in a reactive environment, particularly for scenarios
+ * involving client credentials grant types.
+ *
+ *
+ *
Class Overview:
+ *
+ * This class contains a utility method for creating and configuring a
+ * {@link ReactiveOAuth2AuthorizedClientManager}. The manager is responsible for
+ * handling OAuth2 authorized clients and their respective tokens in a reactive
+ * Spring application.
+ *
+ *
+ *
Methods:
+ *
+ *
createAuthorizedClientManager: Configures and creates an
+ * instance of {@link ReactiveOAuth2AuthorizedClientManager} using the provided
+ * client registration repository and authorized client service.
+ *
+ *
Parameters:
+ *
+ *
clientRegistrationRepository
+ * ({@link ReactiveClientRegistrationRepository}): Repository for managing
+ * client registrations. It provides information about client details such as
+ * client ID, client secret, and scopes.
+ *
authorizedClientService
+ * ({@link ReactiveOAuth2AuthorizedClientService}): Service for managing
+ * authorized clients and their tokens. It handles storing and retrieving
+ * authorized client information.
+ *
+ *
+ *
Returns:
+ *
+ * A {@link ReactiveOAuth2AuthorizedClientManager} instance configured with the
+ * provided client registration repository and authorized client service. This
+ * manager is used to manage and authorize OAuth2 clients.
+ *
+ *
+ *
Usage:
+ *
+ * This method is used to set up an authorized client manager for handling
+ * OAuth2 authentication in a reactive application. The manager will use client
+ * credentials grant type to obtain access tokens and manage OAuth2 clients.
+ *
+ * Ensure that the provided {@link ReactiveClientRegistrationRepository} and
+ * {@link ReactiveOAuth2AuthorizedClientService} are correctly configured and
+ * initialized before passing them to the method. This setup is crucial for
+ * proper OAuth2 client management and token handling.
+ *
+ */
public class ReactiveOAuth2Utils {
+
+ /**
+ * Creates and configures a {@link ReactiveOAuth2AuthorizedClientManager}
+ * instance.
+ *
+ * This method sets up a {@link ReactiveOAuth2AuthorizedClientManager} with a
+ * client credentials provider, using the provided
+ * {@link ReactiveClientRegistrationRepository} and
+ * {@link ReactiveOAuth2AuthorizedClientService}.
+ *
+ *
+ * @param clientRegistrationRepository
+ * The repository for managing client registrations.
+ * @param authorizedClientService
+ * The service for managing authorized clients and their tokens.
+ * @return A configured {@link ReactiveOAuth2AuthorizedClientManager} instance.
+ */
public static ReactiveOAuth2AuthorizedClientManager createAuthorizedClientManager(
ReactiveClientRegistrationRepository clientRegistrationRepository,
ReactiveOAuth2AuthorizedClientService authorizedClientService) {
+
ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials()
diff --git a/src/main/java/io/hoangtien2k3/commons/utils/SQLUtils.java b/src/main/java/io/hoangtien2k3/commons/utils/SQLUtils.java
index c5aaa11..8a5ca33 100644
--- a/src/main/java/io/hoangtien2k3/commons/utils/SQLUtils.java
+++ b/src/main/java/io/hoangtien2k3/commons/utils/SQLUtils.java
@@ -16,7 +16,86 @@
import org.apache.commons.lang3.StringUtils;
+/**
+ * Utility class for handling SQL-related operations.
+ *
+ * The {@code SQLUtils} class provides a static method to replace special
+ * characters in a string to make it suitable for use in SQL queries. This is
+ * particularly useful for escaping characters that have special meanings in SQL
+ * like `%` and `_`, which are used in wildcard patterns.
+ *
+ *
+ *
Class Overview:
+ *
+ * This class contains utility methods for SQL operations. Specifically, it
+ * provides a method to replace special characters in a string that can
+ * interfere with SQL query syntax.
+ *
+ *
+ *
Methods:
+ *
+ *
replaceSpecialDigit: Replaces special characters in a
+ * string to prevent them from being interpreted as SQL wildcards.
+ *
+ *
Parameters:
+ *
+ *
value (String): The string to be processed. It
+ * can be any value that might contain special characters.
+ *
+ *
+ *
Returns:
+ *
+ * The processed string with special characters replaced. Specifically, the
+ * percent sign (`%`) and underscore (`_`) are replaced with their escaped
+ * versions (`\%` and `\_` respectively). If the input string is empty or
+ * null, an empty string is returned.
+ *
+ *
+ *
Usage:
+ *
+ * This method is used to escape special characters in SQL queries to prevent
+ * them from being interpreted as wildcards. For example, if you need to include
+ * a literal percent sign in your SQL query, you would use this method to
+ * replace `%` with `\%`.
+ *
+ * This utility method is designed to handle basic escaping of special
+ * characters for SQL queries. Depending on the SQL database being used,
+ * additional escaping or handling might be required for other special
+ * characters or SQL injection prevention.
+ *
+ */
public class SQLUtils {
+
+ /**
+ * Replaces special characters in a string to prevent them from being
+ * interpreted as SQL wildcards.
+ *
+ * Specifically, the percent sign (`%`) and underscore (`_`) are replaced with
+ * their escaped versions (`\%` and `\_` respectively). This is useful for
+ * ensuring that these characters are treated as literals in SQL queries rather
+ * than wildcards.
+ *
+ *
+ * @param value
+ * The string to be processed. Can be null or empty.
+ * @return The processed string with special characters replaced. Returns an
+ * empty string if the input is null or empty.
+ */
public static String replaceSpecialDigit(String value) {
if (!StringUtils.isEmpty(value)) {
value = value.replace("%", "\\%").replace("_", "\\_");
diff --git a/src/main/java/io/hoangtien2k3/commons/utils/StreamUtil.java b/src/main/java/io/hoangtien2k3/commons/utils/StreamUtil.java
index a5105ee..42a0706 100644
--- a/src/main/java/io/hoangtien2k3/commons/utils/StreamUtil.java
+++ b/src/main/java/io/hoangtien2k3/commons/utils/StreamUtil.java
@@ -18,12 +18,91 @@
import java.io.InputStream;
import lombok.extern.slf4j.Slf4j;
+/**
+ * Utility class for handling stream operations.
+ *
+ * The {@code StreamUtil} class provides a static method for converting an
+ * {@link InputStream} into a byte array. This is useful for reading data from a
+ * stream and processing it as a byte array.
+ *
+ *
+ *
Class Overview:
+ *
+ * This class contains utility methods for working with streams. Specifically,
+ * it provides a method to read data from an {@code InputStream} and convert it
+ * into a byte array.
+ *
+ *
+ *
Methods:
+ *
+ *
streamToByteArray: Reads all the bytes from an
+ * {@code InputStream} and returns them as a byte array.
+ *
+ *
Parameters:
+ *
+ *
inStream (InputStream): The input stream to be
+ * read.
+ *
+ *
+ *
Returns:
+ *
+ * A byte array containing the data read from the input stream. If an error
+ * occurs during reading, an empty byte array is returned.
+ *
+ *
+ *
Usage:
+ *
+ * This method is used to read data from an input stream into a byte array. This
+ * is useful when you need to process or manipulate the entire contents of a
+ * stream as a byte array.
+ *
+ *
+ *
Exceptions:
+ *
+ * If an error occurs during the reading process (e.g., an {@link Exception} is
+ * thrown), an error message is logged, and an empty byte array is returned.
+ *
+ *
+ *
+ *
+ *
+ *
+ *
Usage Example:
+ *
+ *
{@code
+ * InputStream inputStream = new FileInputStream("path/to/file");
+ * byte[] data = StreamUtil.streamToByteArray(inputStream);
+ * // data now contains the byte array representation of the file
+ * }
+ *
+ *
Notes:
+ *
+ * The buffer size used for reading the stream is 100 bytes. If the stream
+ * contains more data, it will be read in chunks until the end of the stream is
+ * reached. The method ensures that all data from the input stream is captured
+ * in the resulting byte array.
+ *
+ */
@Slf4j
public class StreamUtil {
+
+ /**
+ * Converts an {@link InputStream} into a byte array.
+ *
+ * This method reads data from the input stream in chunks and writes it to a
+ * {@link ByteArrayOutputStream}. Once all data has been read, it is converted
+ * into a byte array and returned.
+ *
+ *
+ * @param inStream
+ * The input stream to be read. Must not be null.
+ * @return A byte array containing the data read from the input stream. If an
+ * error occurs, an empty byte array is returned.
+ */
public static byte[] streamToByteArray(InputStream inStream) {
ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
byte[] buff = new byte[100];
- int rc = 0;
+ int rc;
byte[] in_b = new byte[] {};
try {
while ((rc = inStream.read(buff, 0, 100)) > 0) {
diff --git a/src/main/java/io/hoangtien2k3/commons/utils/TruncateUtils.java b/src/main/java/io/hoangtien2k3/commons/utils/TruncateUtils.java
index e87079f..d69b0d0 100644
--- a/src/main/java/io/hoangtien2k3/commons/utils/TruncateUtils.java
+++ b/src/main/java/io/hoangtien2k3/commons/utils/TruncateUtils.java
@@ -20,8 +20,153 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.MultiValueMap;
+/**
+ * Utility class for truncating strings and handling serialization of objects.
+ *
+ * The {@code TruncateUtils} class provides methods for truncating a string to
+ * ensure it does not exceed a specified byte length when encoded in UTF-8. It
+ * also includes methods for serializing objects and handling form data
+ * truncation.
+ *
+ *
+ *
Class Overview:
+ *
+ * This class contains methods to truncate strings based on byte length,
+ * serialize objects to strings, and handle form data truncation.
+ *
+ *
+ *
Methods:
+ *
+ *
truncate: Truncates a string to fit within a specified
+ * byte length when encoded in UTF-8.
+ *
+ *
Parameters:
+ *
+ *
{@code s} ({@code String}): The string to be truncated. Can be
+ * {@code null} or empty.
+ *
{@code maxByte} ({@code int}): The maximum byte length for the truncated
+ * string.
+ *
+ *
+ *
Returns:
+ *
+ * A truncated version of the input string if it exceeds the specified byte
+ * length, otherwise the original string.
+ *
+ *
+ *
Exceptions:
+ *
+ * Logs an error if an exception occurs during the truncation process. Returns
+ * the original string if an error occurs.
+ *
+ *
+ *
+ *
+ *
truncateBody: Truncates a string to fit within a
+ * specified byte length when encoded in UTF-8.
+ *
+ *
Parameters:
+ *
+ *
{@code s} ({@code String}): The string to be truncated.
+ *
{@code maxByte} ({@code int}): The maximum byte length for the truncated
+ * string.
+ *
+ *
+ *
Returns:
+ *
+ * A truncated version of the input string if it exceeds the specified byte
+ * length, otherwise the original string.
+ *
+ *
+ *
Notes:
+ *
+ * This method calculates the number of bytes required for each character in the
+ * string to ensure that the truncation does not break multi-byte characters.
+ *
+ *
+ *
+ *
+ *
truncateBody(Object responseBody): Serializes an object
+ * to a JSON string and truncates it.
+ *
+ *
Parameters:
+ *
+ *
{@code responseBody} ({@code Object}): The object to be serialized and
+ * truncated.
+ *
+ *
+ *
Returns:
+ *
+ * A JSON representation of the object, truncated if necessary. If serialization
+ * fails, returns a placeholder string.
+ *
+ *
+ *
Exceptions:
+ *
+ * Logs an error if an exception occurs during serialization. Returns a
+ * placeholder string if an error occurs.
+ *
+ *
+ *
+ *
+ *
truncateBody(MultiValueMap formData):
+ * Truncates and concatenates form data.
+ *
+ *
Parameters:
+ *
+ *
{@code formData} ({@code MultiValueMap}): The form data
+ * to be truncated and concatenated.
+ *
+ *
+ *
Returns:
+ *
+ * A concatenated string representation of the form data, with values truncated
+ * as necessary.
+ *
+ *
+ *
+ *
+ *
+ *
+ *
Usage Example:
+ *
+ *
{@code
+ * String longString = "A very long string that needs to be truncated...";
+ * String truncatedString = TruncateUtils.truncate(longString, 50);
+ * // truncatedString will contain the first 50 bytes of the original string
+ *
+ * Object responseObject = new SomeObject();
+ * String jsonString = TruncateUtils.truncateBody(responseObject);
+ * // jsonString will contain the JSON representation of responseObject
+ * }
+ *
+ *
Notes:
+ *
+ * The UTF-8 byte length calculation considers multi-byte characters to ensure
+ * that truncation does not cut characters in the middle. The class uses
+ * {@code ObjectMapper} for serialization of objects and handles exceptions
+ * gracefully by logging errors and returning default values when necessary.
+ *
+ */
@Slf4j
public class TruncateUtils {
+
+ /**
+ * Truncates a string to fit within a specified byte length when encoded in
+ * UTF-8.
+ *
+ * This method first checks if the string is null or empty, and if so, returns
+ * it as is. If the string is longer than the specified byte length, it is
+ * truncated while preserving multi-byte characters.
+ *
+ *
+ * @param s
+ * The string to be truncated. Can be null or empty.
+ * @param maxByte
+ * The maximum byte length for the truncated string.
+ * @return A truncated version of the input string if it exceeds the specified
+ * byte length, otherwise the original string.
+ */
public static String truncate(String s, int maxByte) {
try {
if (DataUtil.isNullOrEmpty(s)) {
@@ -38,6 +183,22 @@ public static String truncate(String s, int maxByte) {
return s;
}
+ /**
+ * Truncates a string to fit within a specified byte length when encoded in
+ * UTF-8.
+ *
+ * This method iterates over the characters in the string, calculating the byte
+ * length for each character and truncating the string when the total byte
+ * length exceeds the specified limit.
+ *
+ *
+ * @param s
+ * The string to be truncated.
+ * @param maxByte
+ * The maximum byte length for the truncated string.
+ * @return A truncated version of the input string if it exceeds the specified
+ * byte length, otherwise the original string.
+ */
public static String truncateBody(String s, int maxByte) {
int b = 0;
for (int i = 0; i < s.length(); i++) {
@@ -69,6 +230,20 @@ public static String truncateBody(String s, int maxByte) {
return s;
}
+ /**
+ * Serializes an object to a JSON string and truncates it to fit within a
+ * specified byte length.
+ *
+ * This method uses Jackson's `ObjectMapper` to convert the object to a JSON
+ * string and then truncates it. If serialization fails, a placeholder string is
+ * returned.
+ *
+ *
+ * @param responseBody
+ * The object to be serialized and truncated.
+ * @return A JSON representation of the object, truncated if necessary. Returns
+ * a placeholder string if serialization fails.
+ */
public static String truncateBody(Object responseBody) {
ObjectMapper objectMapper = new ObjectMapper();
try {
@@ -79,11 +254,21 @@ public static String truncateBody(Object responseBody) {
}
}
+ /**
+ * Truncates and concatenates form data.
+ *
+ * This method iterates over the entries in a {@link MultiValueMap}, truncates
+ * the values, and concatenates them into a single string representation.
+ *
+ *
+ * @param formData
+ * The form data to be truncated and concatenated.
+ */
private String truncateBody(MultiValueMap formData) {
StringBuilder messageResponse = new StringBuilder();
Set keys = formData.keySet();
for (String key : keys) {
- messageResponse.append(key + ":" + truncateBody(formData.get(key)));
+ messageResponse.append(key).append(":").append(truncateBody(formData.get(key)));
}
return messageResponse.toString();
}