Apereo CAS Core Utilities - A comprehensive utility library providing functional programming constructs, encryption utilities, configuration helpers, and core infrastructure components for the Central Authentication Service framework
—
Advanced functional programming utilities with enhanced exception handling, conditional operations, retry mechanisms, and composable functions for robust and elegant code patterns.
import org.apereo.cas.util.function.FunctionUtils;
import org.jooq.lambda.fi.util.function.CheckedFunction;
import org.jooq.lambda.fi.util.function.CheckedConsumer;
import org.jooq.lambda.fi.util.function.CheckedSupplier;
import org.springframework.retry.RetryCallback;
import java.util.function.*;
import java.util.List;Comprehensive utility class providing functional programming constructs with exception handling and conditional execution patterns.
@UtilityClass
public class FunctionUtils {
// Conditional Functions - Return modified values based on conditions
public static <T, R> Function<T, R> doIf(Predicate<Object> condition,
Supplier<R> trueFunction,
Supplier<R> falseFunction);
public static <T, R> Function<T, R> doIf(Predicate<T> condition,
CheckedFunction<T, R> trueFunction,
CheckedFunction<T, R> falseFunction);
// Conditional Consumers - Execute actions based on conditions
public static <T> Consumer<T> doIf(boolean condition,
Consumer<T> trueFunction,
Consumer<T> falseFunction);
public static <T> Consumer<T> doIf(boolean condition,
Consumer<T> trueFunction);
// Conditional Suppliers - Supply values based on conditions
public static <R> Supplier<R> doIf(boolean condition,
Supplier<R> trueFunction,
Supplier<R> falseFunction);
// String-specific conditional operations
public static <T> void doIfBlank(CharSequence input, CheckedConsumer<T> trueFunction);
public static <T> T doIfNotBlank(CharSequence input,
CheckedSupplier<T> trueFunction,
CheckedSupplier<T> falseFunction);
public static <T extends CharSequence> void doIfNotBlank(T input, CheckedConsumer<T> trueFunction);
// Null-safe operations
public static <R> R doIfNotNull(Object input, CheckedSupplier<R> trueFunction);
public static <R> Supplier<R> doIfNotNull(Object input,
CheckedSupplier<R> trueFunction,
CheckedSupplier<R> falseFunction);
public static <T> void doIfNotNull(T input, CheckedConsumer<T> trueFunction);
public static <T> void doIfNotNull(T input,
CheckedConsumer<T> trueFunction,
CheckedConsumer<T> falseFunction);
// Null condition operations
public static <T> void doIfNull(T input, CheckedConsumer<T> trueFunction);
public static <T> void doIfNull(T input,
CheckedConsumer<T> trueFunction,
CheckedConsumer<T> falseFunction);
public static <R> Supplier<R> doIfNull(Object input,
Supplier<R> trueFunction,
Supplier<R> falseFunction);
public static <T> void doIfNotNull(T obj, Consumer<T> consumer);
public static <T> void doIfNull(T obj, Runnable action);
// Exception handling with functions
public static <T, R> Function<T, R> doAndHandle(CheckedFunction<T, R> function,
CheckedFunction<Throwable, R> errorHandler);
public static <R> Consumer<R> doAndHandle(CheckedConsumer<R> function,
CheckedFunction<Throwable, R> errorHandler);
public static <R> R doAndHandle(CheckedSupplier<R> function);
public static <R> void doAndHandle(CheckedConsumer<R> function);
public static <R> Supplier<R> doAndHandle(CheckedSupplier<R> function,
CheckedFunction<Throwable, R> errorHandler);
// Conditional execution with predicates
public static <T> void doWhen(T obj, Predicate<T> condition, Consumer<T> consumer);
// Exception-safe operations
public static boolean doWithoutThrows(Runnable action);
// Unchecked exception handling
public static <T> T doUnchecked(Supplier<T> supplier);
public static void doUnchecked(Runnable action);
// Retry operations
public static <T> T doAndRetry(RetryCallback<T, Exception> callback) throws Exception;
public static <T> T doAndRetry(RetryCallback<T, Exception> callback, int maximumAttempts) throws Exception;
public static <T> T doAndRetry(List<Class<? extends Throwable>> clazzes,
RetryCallback<T, Exception> callback) throws Exception;
public static <T> T doAndRetry(List<Class<? extends Throwable>> clazzes,
RetryCallback<T, Exception> callback,
int maximumAttempts) throws Exception;
// Validation helpers
public static String throwIfBlank(String str, String message);
public static <T> T throwIfNull(T obj, String message);
public static void throwIf(BooleanSupplier condition, String message);
// Return value helpers
public static <T> T doAndReturn(Runnable action, T returnValue);
public static <T> T doAndThrow(Runnable action, RuntimeException exception);
public static <T> T doAndThrowUnchecked(Runnable action, Exception exception);
}Conditional operations with enhanced readability:
@Service
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
private final EmailService emailService;
public void processUserRegistration(UserRegistrationRequest request) {
// Conditional validation with clear semantics
String email = FunctionUtils.throwIfBlank(request.getEmail(), "Email is required");
String username = FunctionUtils.throwIfBlank(request.getUsername(), "Username is required");
// Conditional processing based on user type
Function<UserRegistrationRequest, User> userCreator = FunctionUtils.doIf(
req -> req.getAccountType() == AccountType.PREMIUM,
req -> createPremiumUser(req),
req -> createStandardUser(req)
);
User user = userCreator.apply(request);
userRepository.save(user);
// Conditional email sending
Consumer<User> emailSender = FunctionUtils.doIf(
u -> u.isEmailNotificationEnabled(),
u -> emailService.sendWelcomeEmail(u.getEmail())
);
emailSender.accept(user);
}
public Optional<UserProfile> getUserProfile(String userId) {
return FunctionUtils.doIfNotBlank(userId, id -> {
User user = userRepository.findById(id);
return FunctionUtils.doIfNotNull(user, this::createUserProfile);
});
}
}Exception handling with graceful degradation:
@Service
public class ExternalApiService {
private final HttpClient httpClient;
private final CacheManager cacheManager;
public ApiResponse fetchUserData(String userId) {
// Primary operation with exception handling and fallback
Function<String, ApiResponse> fetchFromApi = FunctionUtils.doAndHandle(
id -> callExternalApi(id),
IOException.class,
ex -> {
log.warn("API call failed, trying cache", ex);
return getCachedResponse(id);
}
);
// Retry mechanism for critical operations
return FunctionUtils.doAndRetry(
() -> fetchFromApi.apply(userId),
3, // Max retries
1000, // Delay between retries
IOException.class, // Retryable exceptions
TimeoutException.class
);
}
public UserPreferences getUserPreferences(String userId) {
// Multiple fallback strategies
Supplier<UserPreferences> preferenceSupplier = FunctionUtils.doIf(
() -> isUserCacheEnabled(),
() -> getCachedPreferences(userId),
() -> getDefaultPreferences()
);
return FunctionUtils.doAndHandle(
preferenceSupplier,
Exception.class,
ex -> {
log.error("Failed to load user preferences", ex);
return UserPreferences.getDefaults();
}
);
}
public void updateUserSettings(String userId, Map<String, Object> settings) {
// Conditional processing with validation
FunctionUtils.doWhen(settings,
s -> !s.isEmpty(),
s -> {
validateSettings(s);
persistSettings(userId, s);
clearUserCache(userId);
}
);
// Null-safe notification
FunctionUtils.doIfNotNull(settings.get("email"),
email -> notificationService.updateEmailPreference(userId, (String) email)
);
}
}Advanced retry patterns with exponential backoff:
@Component
public class ResilientDataService {
public <T> T executeWithRetry(Supplier<T> operation, String operationName) {
return FunctionUtils.doAndRetry(
() -> {
log.debug("Executing operation: {}", operationName);
T result = operation.get();
log.debug("Operation completed successfully: {}", operationName);
return result;
},
5, // Max retries
1000, // Base delay
SQLException.class, // Database exceptions
ConnectException.class, // Network exceptions
SocketTimeoutException.class // Timeout exceptions
);
}
public CompletableFuture<String> processDataAsync(String dataId) {
return CompletableFuture.supplyAsync(() ->
FunctionUtils.doAndHandle(
() -> processData(dataId),
Exception.class,
ex -> {
log.error("Async data processing failed for: {}", dataId, ex);
return "ERROR: " + ex.getMessage();
}
)
);
}
public void batchProcess(List<String> dataIds) {
dataIds.parallelStream()
.forEach(id ->
FunctionUtils.doWhen(id,
Objects::nonNull,
dataId -> FunctionUtils.doWithoutThrows(() -> processData(dataId))
)
);
}
}Validation and transformation pipelines:
@Service
public class DataValidationService {
public ProcessedData validateAndTransform(RawData rawData) {
// Validation pipeline with early returns
RawData validated = FunctionUtils.doAndReturn(
() -> FunctionUtils.throwIfNull(rawData, "Raw data cannot be null"),
rawData
);
// Transformation pipeline with conditional steps
Function<RawData, ProcessedData> transformationPipeline = data -> {
// Step 1: Basic sanitization
data = FunctionUtils.doIfNotNull(data, this::sanitizeData);
// Step 2: Conditional enrichment
Function<RawData, RawData> enricher = FunctionUtils.doIf(
d -> d.requiresEnrichment(),
d -> enrichData(d),
d -> d // No enrichment needed
);
data = enricher.apply(data);
// Step 3: Format conversion
return convertToProcessedData(data);
};
// Execute with error handling
return FunctionUtils.doAndHandle(
() -> transformationPipeline.apply(validated),
ValidationException.class,
ex -> {
log.error("Validation failed", ex);
throw new ProcessingException("Data validation failed", ex);
}
);
}
public ValidationResult validateBusinessRules(ProcessedData data) {
List<ValidationError> errors = new ArrayList<>();
// Conditional validation rules
Consumer<ProcessedData> validator = FunctionUtils.doIf(
d -> d.getType() == DataType.FINANCIAL,
d -> validateFinancialRules(d, errors)
).andThen(FunctionUtils.doIf(
d -> d.hasPersonalInfo(),
d -> validatePrivacyRules(d, errors)
)).andThen(FunctionUtils.doIf(
d -> d.isExternalSource(),
d -> validateExternalSourceRules(d, errors)
));
validator.accept(data);
return errors.isEmpty() ? ValidationResult.success() : ValidationResult.failure(errors);
}
}Interface extending Function for composable operations with enhanced chaining capabilities.
@FunctionalInterface
public interface ComposableFunction<T, R> extends Function<T, R> {
// Enhanced composition methods
default <V> ComposableFunction<T, V> andThenComposable(Function<? super R, ? extends V> after);
default <V> ComposableFunction<V, R> composeComposable(Function<? super V, ? extends T> before);
// Conditional composition
default ComposableFunction<T, R> composeIf(Predicate<T> condition,
Function<T, T> preprocessor);
// Exception-safe composition
default ComposableFunction<T, Optional<R>> safely();
// Retry composition
default ComposableFunction<T, R> withRetry(int maxRetries, long delayMillis);
// Factory methods
static <T, R> ComposableFunction<T, R> of(Function<T, R> function);
static <T> ComposableFunction<T, T> identity();
}Composable function pipelines:
@Service
public class DataProcessingPipeline {
public ProcessingResult processUserData(UserInput input) {
// Create composable processing pipeline
ComposableFunction<UserInput, ProcessingResult> pipeline = ComposableFunction
.<UserInput>identity()
.andThenComposable(this::validateInput)
.andThenComposable(this::sanitizeInput)
.andThenComposable(this::enrichInput)
.andThenComposable(this::transformInput)
.andThenComposable(this::persistInput)
.andThenComposable(this::createResult);
// Execute pipeline with error handling
return pipeline.safely()
.withRetry(3, 1000)
.apply(input)
.orElse(ProcessingResult.failed("Processing pipeline failed"));
}
public ComposableFunction<String, User> createUserPipeline() {
return ComposableFunction
.<String>of(this::validateUserId)
.composeIf(Objects::nonNull, this::normalizeUserId)
.andThenComposable(this::fetchUserFromRepository)
.andThenComposable(this::enrichUserData)
.safely(); // Make the entire pipeline safe
}
}Advanced composition with conditions:
@Component
public class ConditionalProcessingService {
public String processMessage(Message message, ProcessingContext context) {
// Build conditional processing pipeline
ComposableFunction<Message, String> processor = ComposableFunction
.<Message>identity()
.composeIf(msg -> msg.isEncrypted(), this::decryptMessage)
.composeIf(msg -> msg.needsSanitization(), this::sanitizeMessage)
.andThenComposable(msg -> applyBusinessRules(msg, context))
.composeIf(msg -> context.isAuditEnabled(), this::auditMessage)
.andThenComposable(this::formatOutput);
return processor.apply(message);
}
public ComposableFunction<Request, Response> createAuthenticationPipeline() {
return ComposableFunction
.<Request>of(this::extractCredentials)
.andThenComposable(this::validateCredentials)
.composeIf(creds -> creds.isMfaRequired(), this::validateMfaToken)
.andThenComposable(this::createAuthenticationResult)
.andThenComposable(this::generateTokens)
.andThenComposable(this::createResponse)
.withRetry(2, 500); // Retry on transient failures
}
}@Service
@Slf4j
public class FunctionalConfigurationService {
private final Map<String, Supplier<Object>> configurationSuppliers;
public FunctionalConfigurationService() {
this.configurationSuppliers = new ConcurrentHashMap<>();
initializeDefaultSuppliers();
}
private void initializeDefaultSuppliers() {
// Database configuration with fallback
registerConfigSupplier("database.url",
FunctionUtils.doIf(
() -> isDatabaseAvailable(),
() -> getDatabaseUrl(),
() -> getDefaultDatabaseUrl()
)
);
// Cache configuration with validation
registerConfigSupplier("cache.enabled",
FunctionUtils.doAndHandle(
() -> Boolean.parseBoolean(getProperty("cache.enabled", "true")),
Exception.class,
ex -> {
log.warn("Failed to parse cache.enabled, using default", ex);
return true;
}
)
);
// External service URLs with retry
registerConfigSupplier("external.api.url",
() -> FunctionUtils.doAndRetry(
() -> validateAndGetExternalUrl(),
3,
2000,
ConnectException.class
)
);
}
public <T> T getConfiguration(String key, Class<T> type) {
Supplier<Object> supplier = configurationSuppliers.get(key);
return FunctionUtils.doIfNotNull(supplier,
s -> FunctionUtils.doAndHandle(
() -> type.cast(s.get()),
ClassCastException.class,
ex -> {
log.error("Configuration cast failed for key: {}", key, ex);
return null;
}
)
);
}
public void registerConfigSupplier(String key, Supplier<Object> supplier) {
configurationSuppliers.put(key, supplier);
}
// Functional configuration validation
public ValidationResult validateAllConfigurations() {
List<String> errors = configurationSuppliers.entrySet()
.parallelStream()
.map(entry -> validateConfiguration(entry.getKey(), entry.getValue()))
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
return errors.isEmpty() ?
ValidationResult.success() :
ValidationResult.failure(errors);
}
private Optional<String> validateConfiguration(String key, Supplier<Object> supplier) {
return FunctionUtils.doAndHandle(
() -> {
Object value = supplier.get();
FunctionUtils.throwIfNull(value, "Configuration value is null");
return Optional.<String>empty();
},
Exception.class,
ex -> Optional.of("Configuration '" + key + "' failed: " + ex.getMessage())
);
}
}@Component
public class FunctionalEventProcessor {
private final Map<EventType, ComposableFunction<Event, ProcessingResult>> processors;
public FunctionalEventProcessor() {
this.processors = new HashMap<>();
initializeProcessors();
}
private void initializeProcessors() {
// User event processor
processors.put(EventType.USER_ACTION,
ComposableFunction
.<Event>of(this::validateUserEvent)
.composeIf(e -> e.requiresAuthentication(), this::authenticateEvent)
.andThenComposable(this::enrichUserEvent)
.composeIf(e -> e.isAuditable(), this::auditEvent)
.andThenComposable(this::processUserAction)
.safely()
.withRetry(2, 1000)
);
// System event processor
processors.put(EventType.SYSTEM_EVENT,
ComposableFunction
.<Event>of(this::validateSystemEvent)
.andThenComposable(this::enrichSystemEvent)
.andThenComposable(this::processSystemEvent)
.safely()
);
// Security event processor
processors.put(EventType.SECURITY_EVENT,
ComposableFunction
.<Event>of(this::validateSecurityEvent)
.andThenComposable(this::enrichSecurityEvent)
.composeIf(e -> e.isCritical(), this::alertSecurityTeam)
.andThenComposable(this::processSecurityEvent)
.safely()
.withRetry(5, 500) // More aggressive retry for security events
);
}
public CompletableFuture<ProcessingResult> processEventAsync(Event event) {
return CompletableFuture.supplyAsync(() ->
FunctionUtils.doIfNotNull(event,
e -> {
ComposableFunction<Event, ProcessingResult> processor =
processors.get(e.getType());
return FunctionUtils.doIfNotNull(processor,
p -> p.apply(e)
);
}
)
);
}
public void processEventBatch(List<Event> events) {
events.parallelStream()
.forEach(event ->
FunctionUtils.doWhen(event,
Objects::nonNull,
e -> FunctionUtils.doWithoutThrows(() ->
processEventAsync(e).get(5, TimeUnit.SECONDS)
)
)
);
}
}This functional programming library provides powerful abstractions for writing clean, composable, and resilient code with comprehensive error handling and conditional logic support, essential for building robust CAS applications.
Install with Tessl CLI
npx tessl i tessl/maven-org-apereo-cas--cas-server-core-util-api