// Base API Response wrapper
public class ApiResponse<T> {
private boolean success;
private String message;
private T data;
private List<String> errors;
public ApiResponse() {
this.errors = new ArrayList<>();
}
public static <T> ApiResponse<T> success(T data) {
ApiResponse<T> response = new ApiResponse<>();
response.success = true;
response.data = data;
response.message = "Operation completed successfully";
return response;
}
public static <T> ApiResponse<T> success(T data, String message) {
ApiResponse<T> response = new ApiResponse<>();
response.success = true;
response.data = data;
response.message = message;
return response;
}
public static <T> ApiResponse<T> error(String message) {
ApiResponse<T> response = new ApiResponse<>();
response.success = false;
response.message = message;
return response;
}
public static <T> ApiResponse<T> error(List<String> errors) {
ApiResponse<T> response = new ApiResponse<>();
response.success = false;
response.errors = errors;
response.message = "Validation failed";
return response;
}
// Getters and setters
public boolean isSuccess() { return success; }
public void setSuccess(boolean success) { this.success = success; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public T getData() { return data; }
public void setData(T data) { this.data = data; }
public List<String> getErrors() { return errors; }
public void setErrors(List<String> errors) { this.errors = errors; }
}
// Custom exceptions
public class ValidationException extends Exception {
private List<String> errors;
public ValidationException(String message) {
super(message);
this.errors = Arrays.asList(message);
}
public ValidationException(List<String> errors) {
super("Validation failed");
this.errors = errors;
}
public List<String> getErrors() { return errors; }
}
public class BusinessLogicException extends Exception {
public BusinessLogicException(String message) {
super(message);
}
public BusinessLogicException(String message, Throwable cause) {
super(message, cause);
}
}
// Validation interface
@FunctionalInterface
public interface Validator<T> {
List<String> validate(T input);
}
// Base API Service abstract class
public abstract class BaseApiService<REQUEST, RESPONSE> {
private static final Logger logger = LoggerFactory.getLogger(BaseApiService.class);
// Template method for processing API requests
public final ApiResponse<RESPONSE> processRequest(REQUEST request) {
String operationName = getOperationName();
long startTime = System.currentTimeMillis();
try {
logger.info("Starting operation: {} with request: {}", operationName, logRequest(request));
// Step 1: Validate input
validateInput(request);
logger.debug("Validation passed for operation: {}", operationName);
// Step 2: Execute business logic
RESPONSE result = executeBusinessLogic(request);
logger.debug("Business logic completed for operation: {}", operationName);
// Step 3: Post-process if needed
RESPONSE finalResult = postProcess(result, request);
long duration = System.currentTimeMillis() - startTime;
logger.info("Operation {} completed successfully in {}ms", operationName, duration);
return ApiResponse.success(finalResult, "Operation completed successfully");
} catch (ValidationException e) {
logger.warn("Validation failed for operation {}: {}", operationName, e.getErrors());
return ApiResponse.error(e.getErrors());
} catch (BusinessLogicException e) {
logger.error("Business logic error in operation {}: {}", operationName, e.getMessage(), e);
return ApiResponse.error("Business logic error: " + e.getMessage());
} catch (Exception e) {
long duration = System.currentTimeMillis() - startTime;
logger.error("Unexpected error in operation {} after {}ms: {}", operationName, duration, e.getMessage(), e);
return ApiResponse.error("Internal server error occurred");
}
}
// Abstract methods to be implemented by concrete services
protected abstract String getOperationName();
protected abstract RESPONSE executeBusinessLogic(REQUEST request) throws BusinessLogicException;
// Default implementations that can be overridden
protected void validateInput(REQUEST request) throws ValidationException {
List<String> errors = getValidator().validate(request);
if (!errors.isEmpty()) {
throw new ValidationException(errors);
}
}
protected RESPONSE postProcess(RESPONSE response, REQUEST request) {
// Default: no post-processing
return response;
}
protected String logRequest(REQUEST request) {
// Override this method to customize request logging (e.g., mask sensitive data)
return request != null ? request.toString() : "null";
}
// Default validator - override in concrete implementations
protected Validator<REQUEST> getValidator() {
return (request) -> {
List<String> errors = new ArrayList<>();
if (request == null) {
errors.add("Request cannot be null");
}
return errors;
};
}
// Utility method for conditional validation
protected void addErrorIf(List<String> errors, boolean condition, String errorMessage) {
if (condition) {
errors.add(errorMessage);
}
}
// Utility method for string validation
protected boolean isNullOrEmpty(String value) {
return value == null || value.trim().isEmpty();
}
// Utility method for collection validation
protected boolean isNullOrEmpty(Collection<?> collection) {
return collection == null || collection.isEmpty();
}
}
// Example concrete implementation
public class UserRegistrationService extends BaseApiService<UserRegistrationRequest, UserRegistrationResponse> {
private static final Logger logger = LoggerFactory.getLogger(UserRegistrationService.class);
@Override
protected String getOperationName() {
return "USER_REGISTRATION";
}
@Override
protected UserRegistrationResponse executeBusinessLogic(UserRegistrationRequest request) throws BusinessLogicException {
try {
// Your business logic here
logger.debug("Processing user registration for email: {}", request.getEmail());
// Example: Check if user already exists
// if (userRepository.existsByEmail(request.getEmail())) {
// throw new BusinessLogicException("User with this email already exists");
// }
// Example: Create user
// User user = userRepository.save(new User(request.getEmail(), request.getName()));
return new UserRegistrationResponse("user123", "Registration successful");
} catch (Exception e) {
throw new BusinessLogicException("Failed to register user", e);
}
}
@Override
protected Validator<UserRegistrationRequest> getValidator() {
return (request) -> {
List<String> errors = new ArrayList<>();
addErrorIf(errors, request == null, "Request cannot be null");
if (request != null) {
addErrorIf(errors, isNullOrEmpty(request.getEmail()), "Email is required");
addErrorIf(errors, isNullOrEmpty(request.getName()), "Name is required");
// Email format validation
if (!isNullOrEmpty(request.getEmail()) && !isValidEmail(request.getEmail())) {
errors.add("Invalid email format");
}
// Name length validation
if (!isNullOrEmpty(request.getName()) && request.getName().length() < 2) {
errors.add("Name must be at least 2 characters long");
}
}
return errors;
};
}
@Override
protected String logRequest(UserRegistrationRequest request) {
// Mask sensitive information in logs
if (request == null) return "null";
return String.format("UserRegistrationRequest{email='%s', name='%s'}",
maskEmail(request.getEmail()), request.getName());
}
private boolean isValidEmail(String email) {
return email.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$");
}
private String maskEmail(String email) {
if (isNullOrEmpty(email)) return "null";
int atIndex = email.indexOf("@");
if (atIndex > 0) {
return email.substring(0, 1) + "***" + email.substring(atIndex);
}
return "***";
}
}
// Example DTOs
public class UserRegistrationRequest {
private String email;
private String name;
private String password;
// Constructors
public UserRegistrationRequest() {}
public UserRegistrationRequest(String email, String name, String password) {
this.email = email;
this.name = name;
this.password = password;
}
// Getters and setters
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}
public class UserRegistrationResponse {
private String userId;
private String message;
public UserRegistrationResponse() {}
public UserRegistrationResponse(String userId, String message) {
this.userId = userId;
this.message = message;
}
// Getters and setters
public String getUserId() { return userId; }
public void setUserId(String userId) { this.userId = userId; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
}
// Example REST Controller usage
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserRegistrationService userRegistrationService;
public UserController(UserRegistrationService userRegistrationService) {
this.userRegistrationService = userRegistrationService;
}
@PostMapping("/register")
public ResponseEntity<ApiResponse<UserRegistrationResponse>> registerUser(
@RequestBody UserRegistrationRequest request) {
ApiResponse<UserRegistrationResponse> response = userRegistrationService.processRequest(request);
HttpStatus status = response.isSuccess() ? HttpStatus.OK : HttpStatus.BAD_REQUEST;
return ResponseEntity.status(status).body(response);
}
}