or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-utilities.mdcryptography.mdfunctional-programming.mdgenerators.mdhttp-clients.mdindex.mdjwt-utilities.mdserialization.mdspecialized-utilities.mdspring-integration.mdtext-processing.md

functional-programming.mddocs/

0

# Functional Programming

1

2

Advanced functional programming utilities with enhanced exception handling, conditional operations, retry mechanisms, and composable functions for robust and elegant code patterns.

3

4

## Core Imports

5

6

```java

7

import org.apereo.cas.util.function.FunctionUtils;

8

import org.jooq.lambda.fi.util.function.CheckedFunction;

9

import org.jooq.lambda.fi.util.function.CheckedConsumer;

10

import org.jooq.lambda.fi.util.function.CheckedSupplier;

11

import org.springframework.retry.RetryCallback;

12

import java.util.function.*;

13

import java.util.List;

14

```

15

16

## FunctionUtils

17

18

Comprehensive utility class providing functional programming constructs with exception handling and conditional execution patterns.

19

20

```java { .api }

21

@UtilityClass

22

public class FunctionUtils {

23

24

// Conditional Functions - Return modified values based on conditions

25

public static <T, R> Function<T, R> doIf(Predicate<Object> condition,

26

Supplier<R> trueFunction,

27

Supplier<R> falseFunction);

28

29

public static <T, R> Function<T, R> doIf(Predicate<T> condition,

30

CheckedFunction<T, R> trueFunction,

31

CheckedFunction<T, R> falseFunction);

32

33

// Conditional Consumers - Execute actions based on conditions

34

public static <T> Consumer<T> doIf(boolean condition,

35

Consumer<T> trueFunction,

36

Consumer<T> falseFunction);

37

38

public static <T> Consumer<T> doIf(boolean condition,

39

Consumer<T> trueFunction);

40

41

// Conditional Suppliers - Supply values based on conditions

42

public static <R> Supplier<R> doIf(boolean condition,

43

Supplier<R> trueFunction,

44

Supplier<R> falseFunction);

45

46

// String-specific conditional operations

47

public static <T> void doIfBlank(CharSequence input, CheckedConsumer<T> trueFunction);

48

49

public static <T> T doIfNotBlank(CharSequence input,

50

CheckedSupplier<T> trueFunction,

51

CheckedSupplier<T> falseFunction);

52

53

public static <T extends CharSequence> void doIfNotBlank(T input, CheckedConsumer<T> trueFunction);

54

55

// Null-safe operations

56

public static <R> R doIfNotNull(Object input, CheckedSupplier<R> trueFunction);

57

58

public static <R> Supplier<R> doIfNotNull(Object input,

59

CheckedSupplier<R> trueFunction,

60

CheckedSupplier<R> falseFunction);

61

62

public static <T> void doIfNotNull(T input, CheckedConsumer<T> trueFunction);

63

64

public static <T> void doIfNotNull(T input,

65

CheckedConsumer<T> trueFunction,

66

CheckedConsumer<T> falseFunction);

67

68

// Null condition operations

69

public static <T> void doIfNull(T input, CheckedConsumer<T> trueFunction);

70

71

public static <T> void doIfNull(T input,

72

CheckedConsumer<T> trueFunction,

73

CheckedConsumer<T> falseFunction);

74

75

public static <R> Supplier<R> doIfNull(Object input,

76

Supplier<R> trueFunction,

77

Supplier<R> falseFunction);

78

79

public static <T> void doIfNotNull(T obj, Consumer<T> consumer);

80

81

public static <T> void doIfNull(T obj, Runnable action);

82

83

// Exception handling with functions

84

public static <T, R> Function<T, R> doAndHandle(CheckedFunction<T, R> function,

85

CheckedFunction<Throwable, R> errorHandler);

86

87

public static <R> Consumer<R> doAndHandle(CheckedConsumer<R> function,

88

CheckedFunction<Throwable, R> errorHandler);

89

90

public static <R> R doAndHandle(CheckedSupplier<R> function);

91

92

public static <R> void doAndHandle(CheckedConsumer<R> function);

93

94

public static <R> Supplier<R> doAndHandle(CheckedSupplier<R> function,

95

CheckedFunction<Throwable, R> errorHandler);

96

97

// Conditional execution with predicates

98

public static <T> void doWhen(T obj, Predicate<T> condition, Consumer<T> consumer);

99

100

// Exception-safe operations

101

public static boolean doWithoutThrows(Runnable action);

102

103

// Unchecked exception handling

104

public static <T> T doUnchecked(Supplier<T> supplier);

105

106

public static void doUnchecked(Runnable action);

107

108

// Retry operations

109

public static <T> T doAndRetry(RetryCallback<T, Exception> callback) throws Exception;

110

111

public static <T> T doAndRetry(RetryCallback<T, Exception> callback, int maximumAttempts) throws Exception;

112

113

public static <T> T doAndRetry(List<Class<? extends Throwable>> clazzes,

114

RetryCallback<T, Exception> callback) throws Exception;

115

116

public static <T> T doAndRetry(List<Class<? extends Throwable>> clazzes,

117

RetryCallback<T, Exception> callback,

118

int maximumAttempts) throws Exception;

119

120

// Validation helpers

121

public static String throwIfBlank(String str, String message);

122

123

public static <T> T throwIfNull(T obj, String message);

124

125

public static void throwIf(BooleanSupplier condition, String message);

126

127

// Return value helpers

128

public static <T> T doAndReturn(Runnable action, T returnValue);

129

130

public static <T> T doAndThrow(Runnable action, RuntimeException exception);

131

132

public static <T> T doAndThrowUnchecked(Runnable action, Exception exception);

133

}

134

```

135

136

### Usage Examples

137

138

**Conditional operations with enhanced readability:**

139

```java

140

@Service

141

public class UserServiceImpl implements UserService {

142

143

private final UserRepository userRepository;

144

private final EmailService emailService;

145

146

public void processUserRegistration(UserRegistrationRequest request) {

147

// Conditional validation with clear semantics

148

String email = FunctionUtils.throwIfBlank(request.getEmail(), "Email is required");

149

String username = FunctionUtils.throwIfBlank(request.getUsername(), "Username is required");

150

151

// Conditional processing based on user type

152

Function<UserRegistrationRequest, User> userCreator = FunctionUtils.doIf(

153

req -> req.getAccountType() == AccountType.PREMIUM,

154

req -> createPremiumUser(req),

155

req -> createStandardUser(req)

156

);

157

158

User user = userCreator.apply(request);

159

userRepository.save(user);

160

161

// Conditional email sending

162

Consumer<User> emailSender = FunctionUtils.doIf(

163

u -> u.isEmailNotificationEnabled(),

164

u -> emailService.sendWelcomeEmail(u.getEmail())

165

);

166

167

emailSender.accept(user);

168

}

169

170

public Optional<UserProfile> getUserProfile(String userId) {

171

return FunctionUtils.doIfNotBlank(userId, id -> {

172

User user = userRepository.findById(id);

173

return FunctionUtils.doIfNotNull(user, this::createUserProfile);

174

});

175

}

176

}

177

```

178

179

**Exception handling with graceful degradation:**

180

```java

181

@Service

182

public class ExternalApiService {

183

184

private final HttpClient httpClient;

185

private final CacheManager cacheManager;

186

187

public ApiResponse fetchUserData(String userId) {

188

// Primary operation with exception handling and fallback

189

Function<String, ApiResponse> fetchFromApi = FunctionUtils.doAndHandle(

190

id -> callExternalApi(id),

191

IOException.class,

192

ex -> {

193

log.warn("API call failed, trying cache", ex);

194

return getCachedResponse(id);

195

}

196

);

197

198

// Retry mechanism for critical operations

199

return FunctionUtils.doAndRetry(

200

() -> fetchFromApi.apply(userId),

201

3, // Max retries

202

1000, // Delay between retries

203

IOException.class, // Retryable exceptions

204

TimeoutException.class

205

);

206

}

207

208

public UserPreferences getUserPreferences(String userId) {

209

// Multiple fallback strategies

210

Supplier<UserPreferences> preferenceSupplier = FunctionUtils.doIf(

211

() -> isUserCacheEnabled(),

212

() -> getCachedPreferences(userId),

213

() -> getDefaultPreferences()

214

);

215

216

return FunctionUtils.doAndHandle(

217

preferenceSupplier,

218

Exception.class,

219

ex -> {

220

log.error("Failed to load user preferences", ex);

221

return UserPreferences.getDefaults();

222

}

223

);

224

}

225

226

public void updateUserSettings(String userId, Map<String, Object> settings) {

227

// Conditional processing with validation

228

FunctionUtils.doWhen(settings,

229

s -> !s.isEmpty(),

230

s -> {

231

validateSettings(s);

232

persistSettings(userId, s);

233

clearUserCache(userId);

234

}

235

);

236

237

// Null-safe notification

238

FunctionUtils.doIfNotNull(settings.get("email"),

239

email -> notificationService.updateEmailPreference(userId, (String) email)

240

);

241

}

242

}

243

```

244

245

**Advanced retry patterns with exponential backoff:**

246

```java

247

@Component

248

public class ResilientDataService {

249

250

public <T> T executeWithRetry(Supplier<T> operation, String operationName) {

251

return FunctionUtils.doAndRetry(

252

() -> {

253

log.debug("Executing operation: {}", operationName);

254

T result = operation.get();

255

log.debug("Operation completed successfully: {}", operationName);

256

return result;

257

},

258

5, // Max retries

259

1000, // Base delay

260

SQLException.class, // Database exceptions

261

ConnectException.class, // Network exceptions

262

SocketTimeoutException.class // Timeout exceptions

263

);

264

}

265

266

public CompletableFuture<String> processDataAsync(String dataId) {

267

return CompletableFuture.supplyAsync(() ->

268

FunctionUtils.doAndHandle(

269

() -> processData(dataId),

270

Exception.class,

271

ex -> {

272

log.error("Async data processing failed for: {}", dataId, ex);

273

return "ERROR: " + ex.getMessage();

274

}

275

)

276

);

277

}

278

279

public void batchProcess(List<String> dataIds) {

280

dataIds.parallelStream()

281

.forEach(id ->

282

FunctionUtils.doWhen(id,

283

Objects::nonNull,

284

dataId -> FunctionUtils.doWithoutThrows(() -> processData(dataId))

285

)

286

);

287

}

288

}

289

```

290

291

**Validation and transformation pipelines:**

292

```java

293

@Service

294

public class DataValidationService {

295

296

public ProcessedData validateAndTransform(RawData rawData) {

297

// Validation pipeline with early returns

298

RawData validated = FunctionUtils.doAndReturn(

299

() -> FunctionUtils.throwIfNull(rawData, "Raw data cannot be null"),

300

rawData

301

);

302

303

// Transformation pipeline with conditional steps

304

Function<RawData, ProcessedData> transformationPipeline = data -> {

305

// Step 1: Basic sanitization

306

data = FunctionUtils.doIfNotNull(data, this::sanitizeData);

307

308

// Step 2: Conditional enrichment

309

Function<RawData, RawData> enricher = FunctionUtils.doIf(

310

d -> d.requiresEnrichment(),

311

d -> enrichData(d),

312

d -> d // No enrichment needed

313

);

314

data = enricher.apply(data);

315

316

// Step 3: Format conversion

317

return convertToProcessedData(data);

318

};

319

320

// Execute with error handling

321

return FunctionUtils.doAndHandle(

322

() -> transformationPipeline.apply(validated),

323

ValidationException.class,

324

ex -> {

325

log.error("Validation failed", ex);

326

throw new ProcessingException("Data validation failed", ex);

327

}

328

);

329

}

330

331

public ValidationResult validateBusinessRules(ProcessedData data) {

332

List<ValidationError> errors = new ArrayList<>();

333

334

// Conditional validation rules

335

Consumer<ProcessedData> validator = FunctionUtils.doIf(

336

d -> d.getType() == DataType.FINANCIAL,

337

d -> validateFinancialRules(d, errors)

338

).andThen(FunctionUtils.doIf(

339

d -> d.hasPersonalInfo(),

340

d -> validatePrivacyRules(d, errors)

341

)).andThen(FunctionUtils.doIf(

342

d -> d.isExternalSource(),

343

d -> validateExternalSourceRules(d, errors)

344

));

345

346

validator.accept(data);

347

348

return errors.isEmpty() ? ValidationResult.success() : ValidationResult.failure(errors);

349

}

350

}

351

```

352

353

## ComposableFunction Interface

354

355

Interface extending Function for composable operations with enhanced chaining capabilities.

356

357

```java { .api }

358

@FunctionalInterface

359

public interface ComposableFunction<T, R> extends Function<T, R> {

360

361

// Enhanced composition methods

362

default <V> ComposableFunction<T, V> andThenComposable(Function<? super R, ? extends V> after);

363

364

default <V> ComposableFunction<V, R> composeComposable(Function<? super V, ? extends T> before);

365

366

// Conditional composition

367

default ComposableFunction<T, R> composeIf(Predicate<T> condition,

368

Function<T, T> preprocessor);

369

370

// Exception-safe composition

371

default ComposableFunction<T, Optional<R>> safely();

372

373

// Retry composition

374

default ComposableFunction<T, R> withRetry(int maxRetries, long delayMillis);

375

376

// Factory methods

377

static <T, R> ComposableFunction<T, R> of(Function<T, R> function);

378

379

static <T> ComposableFunction<T, T> identity();

380

}

381

```

382

383

### Usage Examples

384

385

**Composable function pipelines:**

386

```java

387

@Service

388

public class DataProcessingPipeline {

389

390

public ProcessingResult processUserData(UserInput input) {

391

// Create composable processing pipeline

392

ComposableFunction<UserInput, ProcessingResult> pipeline = ComposableFunction

393

.<UserInput>identity()

394

.andThenComposable(this::validateInput)

395

.andThenComposable(this::sanitizeInput)

396

.andThenComposable(this::enrichInput)

397

.andThenComposable(this::transformInput)

398

.andThenComposable(this::persistInput)

399

.andThenComposable(this::createResult);

400

401

// Execute pipeline with error handling

402

return pipeline.safely()

403

.withRetry(3, 1000)

404

.apply(input)

405

.orElse(ProcessingResult.failed("Processing pipeline failed"));

406

}

407

408

public ComposableFunction<String, User> createUserPipeline() {

409

return ComposableFunction

410

.<String>of(this::validateUserId)

411

.composeIf(Objects::nonNull, this::normalizeUserId)

412

.andThenComposable(this::fetchUserFromRepository)

413

.andThenComposable(this::enrichUserData)

414

.safely(); // Make the entire pipeline safe

415

}

416

}

417

```

418

419

**Advanced composition with conditions:**

420

```java

421

@Component

422

public class ConditionalProcessingService {

423

424

public String processMessage(Message message, ProcessingContext context) {

425

// Build conditional processing pipeline

426

ComposableFunction<Message, String> processor = ComposableFunction

427

.<Message>identity()

428

.composeIf(msg -> msg.isEncrypted(), this::decryptMessage)

429

.composeIf(msg -> msg.needsSanitization(), this::sanitizeMessage)

430

.andThenComposable(msg -> applyBusinessRules(msg, context))

431

.composeIf(msg -> context.isAuditEnabled(), this::auditMessage)

432

.andThenComposable(this::formatOutput);

433

434

return processor.apply(message);

435

}

436

437

public ComposableFunction<Request, Response> createAuthenticationPipeline() {

438

return ComposableFunction

439

.<Request>of(this::extractCredentials)

440

.andThenComposable(this::validateCredentials)

441

.composeIf(creds -> creds.isMfaRequired(), this::validateMfaToken)

442

.andThenComposable(this::createAuthenticationResult)

443

.andThenComposable(this::generateTokens)

444

.andThenComposable(this::createResponse)

445

.withRetry(2, 500); // Retry on transient failures

446

}

447

}

448

```

449

450

## Complete Integration Examples

451

452

### Functional Configuration Service

453

454

```java

455

@Service

456

@Slf4j

457

public class FunctionalConfigurationService {

458

459

private final Map<String, Supplier<Object>> configurationSuppliers;

460

461

public FunctionalConfigurationService() {

462

this.configurationSuppliers = new ConcurrentHashMap<>();

463

initializeDefaultSuppliers();

464

}

465

466

private void initializeDefaultSuppliers() {

467

// Database configuration with fallback

468

registerConfigSupplier("database.url",

469

FunctionUtils.doIf(

470

() -> isDatabaseAvailable(),

471

() -> getDatabaseUrl(),

472

() -> getDefaultDatabaseUrl()

473

)

474

);

475

476

// Cache configuration with validation

477

registerConfigSupplier("cache.enabled",

478

FunctionUtils.doAndHandle(

479

() -> Boolean.parseBoolean(getProperty("cache.enabled", "true")),

480

Exception.class,

481

ex -> {

482

log.warn("Failed to parse cache.enabled, using default", ex);

483

return true;

484

}

485

)

486

);

487

488

// External service URLs with retry

489

registerConfigSupplier("external.api.url",

490

() -> FunctionUtils.doAndRetry(

491

() -> validateAndGetExternalUrl(),

492

3,

493

2000,

494

ConnectException.class

495

)

496

);

497

}

498

499

public <T> T getConfiguration(String key, Class<T> type) {

500

Supplier<Object> supplier = configurationSuppliers.get(key);

501

502

return FunctionUtils.doIfNotNull(supplier,

503

s -> FunctionUtils.doAndHandle(

504

() -> type.cast(s.get()),

505

ClassCastException.class,

506

ex -> {

507

log.error("Configuration cast failed for key: {}", key, ex);

508

return null;

509

}

510

)

511

);

512

}

513

514

public void registerConfigSupplier(String key, Supplier<Object> supplier) {

515

configurationSuppliers.put(key, supplier);

516

}

517

518

// Functional configuration validation

519

public ValidationResult validateAllConfigurations() {

520

List<String> errors = configurationSuppliers.entrySet()

521

.parallelStream()

522

.map(entry -> validateConfiguration(entry.getKey(), entry.getValue()))

523

.filter(Optional::isPresent)

524

.map(Optional::get)

525

.collect(Collectors.toList());

526

527

return errors.isEmpty() ?

528

ValidationResult.success() :

529

ValidationResult.failure(errors);

530

}

531

532

private Optional<String> validateConfiguration(String key, Supplier<Object> supplier) {

533

return FunctionUtils.doAndHandle(

534

() -> {

535

Object value = supplier.get();

536

FunctionUtils.throwIfNull(value, "Configuration value is null");

537

return Optional.<String>empty();

538

},

539

Exception.class,

540

ex -> Optional.of("Configuration '" + key + "' failed: " + ex.getMessage())

541

);

542

}

543

}

544

```

545

546

### Functional Event Processing System

547

548

```java

549

@Component

550

public class FunctionalEventProcessor {

551

552

private final Map<EventType, ComposableFunction<Event, ProcessingResult>> processors;

553

554

public FunctionalEventProcessor() {

555

this.processors = new HashMap<>();

556

initializeProcessors();

557

}

558

559

private void initializeProcessors() {

560

// User event processor

561

processors.put(EventType.USER_ACTION,

562

ComposableFunction

563

.<Event>of(this::validateUserEvent)

564

.composeIf(e -> e.requiresAuthentication(), this::authenticateEvent)

565

.andThenComposable(this::enrichUserEvent)

566

.composeIf(e -> e.isAuditable(), this::auditEvent)

567

.andThenComposable(this::processUserAction)

568

.safely()

569

.withRetry(2, 1000)

570

);

571

572

// System event processor

573

processors.put(EventType.SYSTEM_EVENT,

574

ComposableFunction

575

.<Event>of(this::validateSystemEvent)

576

.andThenComposable(this::enrichSystemEvent)

577

.andThenComposable(this::processSystemEvent)

578

.safely()

579

);

580

581

// Security event processor

582

processors.put(EventType.SECURITY_EVENT,

583

ComposableFunction

584

.<Event>of(this::validateSecurityEvent)

585

.andThenComposable(this::enrichSecurityEvent)

586

.composeIf(e -> e.isCritical(), this::alertSecurityTeam)

587

.andThenComposable(this::processSecurityEvent)

588

.safely()

589

.withRetry(5, 500) // More aggressive retry for security events

590

);

591

}

592

593

public CompletableFuture<ProcessingResult> processEventAsync(Event event) {

594

return CompletableFuture.supplyAsync(() ->

595

FunctionUtils.doIfNotNull(event,

596

e -> {

597

ComposableFunction<Event, ProcessingResult> processor =

598

processors.get(e.getType());

599

600

return FunctionUtils.doIfNotNull(processor,

601

p -> p.apply(e)

602

);

603

}

604

)

605

);

606

}

607

608

public void processEventBatch(List<Event> events) {

609

events.parallelStream()

610

.forEach(event ->

611

FunctionUtils.doWhen(event,

612

Objects::nonNull,

613

e -> FunctionUtils.doWithoutThrows(() ->

614

processEventAsync(e).get(5, TimeUnit.SECONDS)

615

)

616

)

617

);

618

}

619

}

620

```

621

622

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.