or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

array-utilities.mdbuilders.mdconcurrent-utilities.mddate-time-utilities.mdexception-utilities.mdindex.mdmath-utilities.mdobject-utilities.mdstring-utilities.mdvalidation-utilities.md

exception-utilities.mddocs/

0

# Exception Utilities

1

2

Apache Commons Lang provides comprehensive exception handling utilities through ExceptionUtils and contextual exception classes. These utilities offer stack trace analysis, cause extraction, exception chaining, and enhanced error reporting capabilities.

3

4

## Core Exception Classes

5

6

### ExceptionUtils - Exception Analysis and Manipulation

7

8

Provides 35 static methods for analyzing exception hierarchies, extracting stack traces, and handling exception chains:

9

10

```java { .api }

11

import org.apache.commons.lang3.exception.ExceptionUtils;

12

```

13

14

#### Stack Trace Operations

15

16

```java { .api }

17

// Stack trace extraction

18

public static String getStackTrace(Throwable throwable)

19

public static String[] getStackFrames(Throwable throwable)

20

public static List<String> getStackFrameList(Throwable throwable)

21

22

// Root cause stack traces

23

public static String[] getRootCauseStackTrace(Throwable throwable)

24

public static List<String> getRootCauseStackTraceList(Throwable throwable)

25

```

26

27

**Usage Examples:**

28

```java { .api }

29

public class StackTraceExamples {

30

31

public void demonstrateStackTraceOperations() {

32

try {

33

riskyOperation();

34

} catch (Exception e) {

35

// Get complete stack trace as string

36

String fullStackTrace = ExceptionUtils.getStackTrace(e);

37

log.error("Full stack trace:\n{}", fullStackTrace);

38

39

// Get stack trace as array of frames

40

String[] frames = ExceptionUtils.getStackFrames(e);

41

for (int i = 0; i < Math.min(5, frames.length); i++) {

42

log.debug("Frame {}: {}", i, frames[i]);

43

}

44

45

// Get root cause stack trace (if exception has causes)

46

String[] rootFrames = ExceptionUtils.getRootCauseStackTrace(e);

47

log.error("Root cause stack trace has {} frames", rootFrames.length);

48

}

49

}

50

51

// Custom logging with stack trace details

52

public void logExceptionDetails(Exception e) {

53

StringBuilder details = new StringBuilder();

54

details.append("Exception Details:\n");

55

details.append("Type: ").append(e.getClass().getSimpleName()).append("\n");

56

details.append("Message: ").append(e.getMessage()).append("\n");

57

58

// Add stack trace information

59

List<String> stackFrames = ExceptionUtils.getStackFrameList(e);

60

details.append("Stack frames (top 5):\n");

61

62

for (int i = 0; i < Math.min(5, stackFrames.size()); i++) {

63

details.append(" ").append(i + 1).append(". ").append(stackFrames.get(i)).append("\n");

64

}

65

66

log.error(details.toString());

67

}

68

}

69

```

70

71

#### Cause Chain Analysis

72

73

```java { .api }

74

// Root cause extraction

75

public static Throwable getRootCause(Throwable throwable)

76

public static String getRootCauseMessage(Throwable throwable)

77

78

// Cause chain navigation

79

public static Throwable getCause(Throwable throwable)

80

public static Throwable getCause(Throwable throwable, String[] methodNames)

81

public static int getThrowableCount(Throwable throwable)

82

public static Throwable[] getThrowables(Throwable throwable)

83

public static List<Throwable> getThrowableList(Throwable throwable)

84

```

85

86

**Usage Examples:**

87

```java { .api }

88

public class CauseChainExamples {

89

90

public void analyzeCauseChain(Exception exception) {

91

// Get root cause

92

Throwable rootCause = ExceptionUtils.getRootCause(exception);

93

if (rootCause != null) {

94

log.error("Root cause: {} - {}", rootCause.getClass().getSimpleName(), rootCause.getMessage());

95

}

96

97

// Get root cause message (handles null safely)

98

String rootMessage = ExceptionUtils.getRootCauseMessage(exception);

99

log.info("Root cause message: {}", rootMessage);

100

101

// Analyze complete cause chain

102

List<Throwable> throwableChain = ExceptionUtils.getThrowableList(exception);

103

log.info("Exception chain has {} levels:", throwableChain.size());

104

105

for (int i = 0; i < throwableChain.size(); i++) {

106

Throwable t = throwableChain.get(i);

107

log.info(" Level {}: {} - {}", i, t.getClass().getSimpleName(), t.getMessage());

108

}

109

}

110

111

// Find specific exception type in cause chain

112

public <T extends Throwable> T findCause(Throwable throwable, Class<T> causeType) {

113

List<Throwable> chain = ExceptionUtils.getThrowableList(throwable);

114

115

for (Throwable t : chain) {

116

if (causeType.isInstance(t)) {

117

return causeType.cast(t);

118

}

119

}

120

121

return null;

122

}

123

124

// Check if specific exception type exists in cause chain

125

public boolean hasCause(Throwable throwable, Class<? extends Throwable> causeType) {

126

return findCause(throwable, causeType) != null;

127

}

128

129

public void handleDatabaseError(Exception e) {

130

// Look for SQL exceptions in the cause chain

131

SQLException sqlException = findCause(e, SQLException.class);

132

if (sqlException != null) {

133

log.error("Database error - SQL State: {}, Error Code: {}",

134

sqlException.getSQLState(), sqlException.getErrorCode());

135

}

136

137

// Look for connection exceptions

138

if (hasCause(e, ConnectException.class)) {

139

log.error("Network connectivity issue detected in exception chain");

140

}

141

}

142

}

143

```

144

145

#### Exception Message Operations

146

147

```java { .api }

148

// Message extraction

149

public static String getMessage(Throwable th)

150

public static String getStackTrace(Throwable throwable)

151

152

// Throwable iteration

153

public static void forEach(Throwable throwable, Consumer<Throwable> consumer)

154

public static <T extends Throwable> T asRuntimeException(Throwable throwable)

155

```

156

157

**Usage Examples:**

158

```java { .api }

159

public class MessageExamples {

160

161

public String extractBestMessage(Throwable throwable) {

162

// Get the most informative message from exception chain

163

String message = ExceptionUtils.getMessage(throwable);

164

if (StringUtils.isBlank(message)) {

165

// Fall back to root cause message if main message is empty

166

message = ExceptionUtils.getRootCauseMessage(throwable);

167

}

168

169

return StringUtils.defaultString(message, "Unknown error");

170

}

171

172

public void analyzeAllExceptionsInChain(Exception exception) {

173

// Process each exception in the chain

174

ExceptionUtils.forEach(exception, throwable -> {

175

log.debug("Processing exception: {} - {}",

176

throwable.getClass().getSimpleName(),

177

throwable.getMessage());

178

179

// Check for specific patterns

180

if (throwable instanceof TimeoutException) {

181

log.warn("Timeout detected in exception chain");

182

}

183

184

if (throwable instanceof SecurityException) {

185

log.error("Security violation detected: {}", throwable.getMessage());

186

}

187

});

188

}

189

190

// Convert checked exceptions to runtime exceptions

191

public void performOperationUnchecked() {

192

try {

193

performRiskyOperation();

194

} catch (CheckedException e) {

195

// Convert to runtime exception while preserving stack trace

196

throw ExceptionUtils.asRuntimeException(e);

197

}

198

}

199

}

200

```

201

202

### Contextual Exception Classes

203

204

#### ContextedException - Exception with Context Information

205

206

Allows adding contextual information to exceptions without losing the original stack trace:

207

208

```java { .api }

209

import org.apache.commons.lang3.exception.ContextedException;

210

```

211

212

```java { .api }

213

public class ContextedExceptionExample {

214

215

public void processUserData(String userId, Map<String, Object> data) {

216

try {

217

validateUserData(data);

218

updateUserProfile(userId, data);

219

} catch (ValidationException e) {

220

// Add context to the exception

221

ContextedException contextedException = new ContextedException("User data processing failed", e);

222

contextedException.addContextValue("userId", userId);

223

contextedException.addContextValue("dataKeys", data.keySet());

224

contextedException.addContextValue("timestamp", new Date());

225

contextedException.addContextValue("processingNode", getServerNodeId());

226

227

throw contextedException;

228

} catch (DatabaseException e) {

229

ContextedException contextedException = new ContextedException("Database operation failed", e);

230

contextedException.addContextValue("operation", "updateUserProfile");

231

contextedException.addContextValue("userId", userId);

232

contextedException.addContextValue("affectedFields", data.keySet());

233

234

throw contextedException;

235

}

236

}

237

238

public void handleContextedException(ContextedException e) {

239

log.error("Contexted exception occurred: {}", e.getMessage());

240

241

// Access context information

242

for (Pair<String, Object> context : e.getContextEntries()) {

243

log.error(" Context - {}: {}", context.getKey(), context.getValue());

244

}

245

246

// Get specific context values

247

String userId = (String) e.getFirstContextValue("userId");

248

Date timestamp = (Date) e.getFirstContextValue("timestamp");

249

250

if (userId != null) {

251

log.error("Failed operation involved user: {}", userId);

252

}

253

254

// Get formatted context

255

String contextString = e.getFormattedExceptionMessage();

256

log.error("Full context:\n{}", contextString);

257

}

258

}

259

```

260

261

#### ContextedRuntimeException - Runtime Exception with Context

262

263

Similar to ContextedException but extends RuntimeException:

264

265

```java { .api }

266

import org.apache.commons.lang3.exception.ContextedRuntimeException;

267

```

268

269

```java { .api }

270

public class ContextedRuntimeExceptionExample {

271

272

public void processPayment(PaymentRequest request) {

273

try {

274

validatePaymentRequest(request);

275

chargePaymentMethod(request);

276

recordTransaction(request);

277

} catch (PaymentValidationException e) {

278

ContextedRuntimeException runtimeException =

279

new ContextedRuntimeException("Payment processing validation failed", e);

280

281

runtimeException.addContextValue("requestId", request.getId());

282

runtimeException.addContextValue("amount", request.getAmount());

283

runtimeException.addContextValue("currency", request.getCurrency());

284

runtimeException.addContextValue("paymentMethod", request.getPaymentMethod().getType());

285

runtimeException.addContextValue("merchantId", request.getMerchantId());

286

287

throw runtimeException;

288

289

} catch (PaymentGatewayException e) {

290

ContextedRuntimeException runtimeException =

291

new ContextedRuntimeException("Payment gateway communication failed", e);

292

293

runtimeException.addContextValue("gateway", e.getGatewayName());

294

runtimeException.addContextValue("gatewayResponseCode", e.getResponseCode());

295

runtimeException.addContextValue("requestId", request.getId());

296

runtimeException.addContextValue("retryCount", getRetryCount(request.getId()));

297

298

throw runtimeException;

299

}

300

}

301

}

302

```

303

304

### Custom Exception Utilities

305

306

#### Exception Builder Pattern

307

308

```java { .api }

309

public class ExceptionBuilder {

310

311

public static class Builder {

312

private String message;

313

private Throwable cause;

314

private final Map<String, Object> context = new LinkedHashMap<>();

315

316

public Builder message(String message) {

317

this.message = message;

318

return this;

319

}

320

321

public Builder cause(Throwable cause) {

322

this.cause = cause;

323

return this;

324

}

325

326

public Builder context(String key, Object value) {

327

this.context.put(key, value);

328

return this;

329

}

330

331

public Builder userContext(String userId, String operation) {

332

return context("userId", userId)

333

.context("operation", operation)

334

.context("timestamp", new Date());

335

}

336

337

public Builder requestContext(String requestId, String endpoint) {

338

return context("requestId", requestId)

339

.context("endpoint", endpoint)

340

.context("serverNode", getServerNodeId());

341

}

342

343

public ContextedRuntimeException buildRuntime() {

344

ContextedRuntimeException exception = new ContextedRuntimeException(message, cause);

345

context.forEach(exception::addContextValue);

346

return exception;

347

}

348

349

public ContextedException buildChecked() {

350

ContextedException exception = new ContextedException(message, cause);

351

context.forEach(exception::addContextValue);

352

return exception;

353

}

354

}

355

356

public static Builder builder() {

357

return new Builder();

358

}

359

}

360

361

// Usage examples

362

public class ExceptionBuilderUsage {

363

364

public void processOrder(String orderId, String userId) {

365

try {

366

Order order = loadOrder(orderId);

367

validateOrder(order, userId);

368

} catch (OrderNotFoundException e) {

369

throw ExceptionBuilder.builder()

370

.message("Order processing failed - order not found")

371

.cause(e)

372

.userContext(userId, "processOrder")

373

.context("orderId", orderId)

374

.buildRuntime();

375

} catch (ValidationException e) {

376

throw ExceptionBuilder.builder()

377

.message("Order validation failed")

378

.cause(e)

379

.userContext(userId, "validateOrder")

380

.context("orderId", orderId)

381

.context("validationErrors", e.getErrors())

382

.buildRuntime();

383

}

384

}

385

386

public void handleApiRequest(String requestId, String endpoint) {

387

try {

388

processApiRequest(endpoint);

389

} catch (Exception e) {

390

throw ExceptionBuilder.builder()

391

.message("API request processing failed")

392

.cause(e)

393

.requestContext(requestId, endpoint)

394

.context("userAgent", getCurrentUserAgent())

395

.context("clientIp", getCurrentClientIp())

396

.buildRuntime();

397

}

398

}

399

}

400

```

401

402

## Advanced Exception Handling Patterns

403

404

### Exception Chain Analysis Utilities

405

406

```java { .api }

407

public final class ExceptionAnalyzer {

408

409

// Find all exceptions of specific types in the chain

410

public static <T extends Throwable> List<T> findCauses(Throwable throwable, Class<T> causeType) {

411

List<T> causes = new ArrayList<>();

412

ExceptionUtils.forEach(throwable, t -> {

413

if (causeType.isInstance(t)) {

414

causes.add(causeType.cast(t));

415

}

416

});

417

return causes;

418

}

419

420

// Get exception chain summary

421

public static ExceptionSummary summarize(Throwable throwable) {

422

List<Throwable> chain = ExceptionUtils.getThrowableList(throwable);

423

424

return ExceptionSummary.builder()

425

.totalExceptions(chain.size())

426

.rootCause(ExceptionUtils.getRootCause(throwable))

427

.rootCauseMessage(ExceptionUtils.getRootCauseMessage(throwable))

428

.exceptionTypes(chain.stream()

429

.map(t -> t.getClass().getSimpleName())

430

.collect(Collectors.toList()))

431

.hasTimeout(chain.stream().anyMatch(t -> t instanceof TimeoutException))

432

.hasNetworkError(chain.stream().anyMatch(t -> isNetworkException(t)))

433

.hasSecurityError(chain.stream().anyMatch(t -> t instanceof SecurityException))

434

.build();

435

}

436

437

// Extract structured error information

438

public static ErrorInfo extractErrorInfo(Throwable throwable) {

439

ErrorInfo.Builder builder = ErrorInfo.builder()

440

.message(ExceptionUtils.getMessage(throwable))

441

.type(throwable.getClass().getSimpleName())

442

.timestamp(new Date());

443

444

// Add context if available

445

if (throwable instanceof ContextedException) {

446

ContextedException ce = (ContextedException) throwable;

447

Map<String, Object> context = new HashMap<>();

448

ce.getContextEntries().forEach(pair -> context.put(pair.getKey(), pair.getValue()));

449

builder.context(context);

450

}

451

452

// Add root cause information

453

Throwable rootCause = ExceptionUtils.getRootCause(throwable);

454

if (rootCause != null && rootCause != throwable) {

455

builder.rootCauseType(rootCause.getClass().getSimpleName())

456

.rootCauseMessage(rootCause.getMessage());

457

}

458

459

return builder.build();

460

}

461

462

// Check for specific error categories

463

public static boolean isRetryableError(Throwable throwable) {

464

return ExceptionUtils.getThrowableList(throwable).stream()

465

.anyMatch(t ->

466

t instanceof ConnectException ||

467

t instanceof SocketTimeoutException ||

468

t instanceof InterruptedException ||

469

(t instanceof SQLException && isTransientSqlError((SQLException) t))

470

);

471

}

472

473

public static boolean isUserError(Throwable throwable) {

474

return ExceptionUtils.getThrowableList(throwable).stream()

475

.anyMatch(t ->

476

t instanceof IllegalArgumentException ||

477

t instanceof ValidationException ||

478

t instanceof AuthenticationException ||

479

t instanceof AuthorizationException

480

);

481

}

482

483

private static boolean isNetworkException(Throwable throwable) {

484

return throwable instanceof ConnectException ||

485

throwable instanceof UnknownHostException ||

486

throwable instanceof SocketException ||

487

throwable instanceof SocketTimeoutException;

488

}

489

490

private static boolean isTransientSqlError(SQLException e) {

491

// Check for common transient SQL error codes

492

String sqlState = e.getSQLState();

493

return sqlState != null && (

494

sqlState.startsWith("08") || // Connection errors

495

sqlState.startsWith("40") || // Transaction rollback

496

sqlState.equals("HY000") // General error (often transient)

497

);

498

}

499

}

500

```

501

502

### Exception Reporting and Monitoring

503

504

```java { .api }

505

@Component

506

public class ExceptionReportingService {

507

508

private final MeterRegistry meterRegistry;

509

private final NotificationService notificationService;

510

511

// Exception metrics

512

public void reportException(Throwable throwable, String context) {

513

ExceptionSummary summary = ExceptionAnalyzer.summarize(throwable);

514

515

// Record metrics

516

Counter.builder("exceptions.total")

517

.tag("type", throwable.getClass().getSimpleName())

518

.tag("context", context)

519

.tag("has_timeout", String.valueOf(summary.hasTimeout()))

520

.tag("has_network_error", String.valueOf(summary.hasNetworkError()))

521

.register(meterRegistry)

522

.increment();

523

524

// Alert on critical errors

525

if (isCriticalError(throwable)) {

526

AlertInfo alert = createAlert(throwable, context, summary);

527

notificationService.sendAlert(alert);

528

}

529

530

// Log structured error information

531

ErrorInfo errorInfo = ExceptionAnalyzer.extractErrorInfo(throwable);

532

log.error("Exception reported: {}", errorInfo.toJson());

533

}

534

535

private boolean isCriticalError(Throwable throwable) {

536

return ExceptionUtils.getThrowableList(throwable).stream()

537

.anyMatch(t ->

538

t instanceof OutOfMemoryError ||

539

t instanceof StackOverflowError ||

540

t instanceof SecurityException ||

541

(t instanceof SQLException && !ExceptionAnalyzer.isRetryableError(t))

542

);

543

}

544

545

// Exception aggregation for dashboards

546

@Scheduled(fixedRate = 60000) // Every minute

547

public void aggregateExceptionMetrics() {

548

ExceptionMetrics metrics = gatherExceptionMetrics();

549

550

Gauge.builder("exceptions.error_rate")

551

.register(meterRegistry, metrics, m -> m.getErrorRate());

552

553

Gauge.builder("exceptions.unique_types")

554

.register(meterRegistry, metrics, m -> m.getUniqueExceptionTypes());

555

}

556

}

557

```

558

559

### Integration Examples

560

561

#### Spring Boot Global Exception Handler

562

563

```java { .api }

564

@ControllerAdvice

565

public class GlobalExceptionHandler {

566

567

private final ExceptionReportingService reportingService;

568

569

@ExceptionHandler(ContextedException.class)

570

public ResponseEntity<ErrorResponse> handleContextedException(ContextedException e) {

571

reportingService.reportException(e, "web-request");

572

573

ErrorResponse response = ErrorResponse.builder()

574

.message("An error occurred while processing your request")

575

.code("PROCESSING_ERROR")

576

.timestamp(new Date())

577

.details(extractPublicContext(e))

578

.build();

579

580

return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);

581

}

582

583

@ExceptionHandler(ValidationException.class)

584

public ResponseEntity<ErrorResponse> handleValidationException(ValidationException e) {

585

reportingService.reportException(e, "validation");

586

587

ErrorResponse response = ErrorResponse.builder()

588

.message("Validation failed")

589

.code("VALIDATION_ERROR")

590

.timestamp(new Date())

591

.details(Map.of("errors", e.getValidationErrors()))

592

.build();

593

594

return ResponseEntity.badRequest().body(response);

595

}

596

597

@ExceptionHandler(Exception.class)

598

public ResponseEntity<ErrorResponse> handleGenericException(Exception e) {

599

// Wrap in contexted exception with request information

600

ContextedRuntimeException contextedException = ExceptionBuilder.builder()

601

.message("Unexpected error occurred")

602

.cause(e)

603

.requestContext(getCurrentRequestId(), getCurrentEndpoint())

604

.context("userAgent", getCurrentUserAgent())

605

.buildRuntime();

606

607

reportingService.reportException(contextedException, "unexpected");

608

609

ErrorResponse response = ErrorResponse.builder()

610

.message("An unexpected error occurred")

611

.code("INTERNAL_ERROR")

612

.timestamp(new Date())

613

.build();

614

615

return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);

616

}

617

618

private Map<String, Object> extractPublicContext(ContextedException e) {

619

Map<String, Object> publicContext = new HashMap<>();

620

621

// Only include non-sensitive context information

622

for (Pair<String, Object> entry : e.getContextEntries()) {

623

String key = entry.getKey();

624

if (!isSensitiveContextKey(key)) {

625

publicContext.put(key, entry.getValue());

626

}

627

}

628

629

return publicContext;

630

}

631

632

private boolean isSensitiveContextKey(String key) {

633

return key.toLowerCase().contains("password") ||

634

key.toLowerCase().contains("token") ||

635

key.toLowerCase().contains("secret") ||

636

key.toLowerCase().contains("key");

637

}

638

}

639

```

640

641

#### Async Exception Handling

642

643

```java { .api }

644

@Service

645

public class AsyncExceptionHandler {

646

647

@EventListener

648

public void handleAsyncException(AsyncExceptionEvent event) {

649

Throwable exception = event.getException();

650

String taskName = event.getTaskName();

651

652

// Add async context

653

ContextedRuntimeException contextedException = ExceptionBuilder.builder()

654

.message("Async task failed")

655

.cause(exception)

656

.context("taskName", taskName)

657

.context("threadName", Thread.currentThread().getName())

658

.context("executionTime", event.getExecutionTime())

659

.buildRuntime();

660

661

// Report for monitoring

662

exceptionReportingService.reportException(contextedException, "async-task");

663

664

// Decide on retry strategy

665

if (ExceptionAnalyzer.isRetryableError(exception)) {

666

scheduleRetry(taskName, event.getTaskData());

667

} else {

668

handleFailedTask(taskName, contextedException);

669

}

670

}

671

672

@Async

673

public CompletableFuture<Void> handleExceptionAsync(Exception e, String context) {

674

return CompletableFuture.runAsync(() -> {

675

try {

676

processException(e, context);

677

} catch (Exception processingError) {

678

// Avoid infinite loops in exception handling

679

log.error("Error while processing exception: {}", processingError.getMessage());

680

}

681

});

682

}

683

}

684

```

685

686

The exception utilities in Apache Commons Lang provide comprehensive tools for robust error handling, context preservation, and exception analysis that are essential for building maintainable and debuggable Java applications.