or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

error-handling.mdframework-configuration.mdhttp-caching.mdindex.mdjsr310-parameters.mdoptional-handling.mdparameter-handling.mdsession-management.mdvalidation.md

error-handling.mddocs/

0

# Error Handling

1

2

Standardized error handling with consistent error message format and exception mappers for common exceptions. Provides unified error responses and comprehensive exception mapping for JAX-RS resources.

3

4

## Capabilities

5

6

### ErrorMessage

7

8

Standardized error message format for consistent client error responses with JSON serialization support.

9

10

```java { .api }

11

/**

12

* Standardized error message format for HTTP error responses

13

* Provides consistent structure for client error handling

14

*/

15

@JsonInclude(JsonInclude.Include.NON_NULL)

16

public class ErrorMessage {

17

18

/** Creates error message with 500 status and message */

19

public ErrorMessage(String message);

20

21

/** Creates error message with specific status code and message */

22

public ErrorMessage(int code, String message);

23

24

/** Creates error message with status code, message, and details */

25

@JsonCreator

26

public ErrorMessage(@JsonProperty("code") int code,

27

@JsonProperty("message") String message,

28

@JsonProperty("details") String details);

29

30

/** Gets the HTTP status code */

31

@JsonProperty("code")

32

public Integer getCode();

33

34

/** Gets the error message */

35

@JsonProperty("message")

36

public String getMessage();

37

38

/** Gets additional error details */

39

@JsonProperty("details")

40

public String getDetails();

41

}

42

```

43

44

**Usage Examples:**

45

46

```java

47

import io.dropwizard.jersey.errors.ErrorMessage;

48

import jakarta.ws.rs.*;

49

import jakarta.ws.rs.core.Response;

50

51

@Path("/api")

52

public class ApiResource {

53

54

@GET

55

@Path("/users/{id}")

56

public Response getUser(@PathParam("id") String userId) {

57

try {

58

User user = userService.findById(userId);

59

if (user == null) {

60

ErrorMessage error = new ErrorMessage(404, "User not found");

61

return Response.status(404).entity(error).build();

62

}

63

return Response.ok(user).build();

64

} catch (IllegalArgumentException e) {

65

ErrorMessage error = new ErrorMessage(400, "Invalid user ID", e.getMessage());

66

return Response.status(400).entity(error).build();

67

} catch (Exception e) {

68

ErrorMessage error = new ErrorMessage(500, "Internal server error");

69

return Response.status(500).entity(error).build();

70

}

71

}

72

}

73

```

74

75

### Exception Mappers

76

77

Exception mappers that convert common exceptions into appropriate HTTP responses with ErrorMessage format.

78

79

```java { .api }

80

/**

81

* Base exception mapper with logging support

82

* Provides consistent exception handling and logging across mappers

83

* @param <E> the exception type to handle

84

*/

85

public class LoggingExceptionMapper<E extends Throwable> implements ExceptionMapper<E> {

86

87

/** Maps exception to HTTP response with logging */

88

public Response toResponse(E exception);

89

90

/** Logs the exception with appropriate level */

91

protected void logException(E exception);

92

93

/** Gets HTTP status code for the exception */

94

protected Response.Status getStatus(E exception);

95

96

/** Creates error message for the exception */

97

protected ErrorMessage createErrorMessage(E exception);

98

}

99

100

/**

101

* Maps IllegalStateException to HTTP 500 responses

102

*/

103

public class IllegalStateExceptionMapper implements ExceptionMapper<IllegalStateException> {

104

105

/** Maps IllegalStateException to 500 Internal Server Error */

106

public Response toResponse(IllegalStateException exception);

107

}

108

109

/**

110

* Maps EarlyEOFException to appropriate HTTP responses

111

* Handles client disconnection scenarios

112

*/

113

public class EarlyEofExceptionMapper implements ExceptionMapper<EarlyEOFException> {

114

115

/** Maps EarlyEOFException to appropriate response */

116

public Response toResponse(EarlyEOFException exception);

117

}

118

```

119

120

### Error Entity Writer

121

122

Message body writer for serializing ErrorMessage objects to JSON responses.

123

124

```java { .api }

125

/**

126

* Message body writer for ErrorMessage objects

127

* Handles JSON serialization of error responses

128

*/

129

public class ErrorEntityWriter implements MessageBodyWriter<ErrorMessage> {

130

131

/** Checks if this writer can handle the given type */

132

public boolean isWriteable(Class<?> type, Type genericType,

133

Annotation[] annotations, MediaType mediaType);

134

135

/** Writes ErrorMessage to output stream as JSON */

136

public void writeTo(ErrorMessage errorMessage, Class<?> type, Type genericType,

137

Annotation[] annotations, MediaType mediaType,

138

MultivaluedMap<String, Object> httpHeaders,

139

OutputStream entityStream) throws IOException;

140

}

141

```

142

143

### EOF Exception Handling

144

145

Specialized handling for early EOF exceptions that occur when clients disconnect during response writing.

146

147

```java { .api }

148

/**

149

* Writer interceptor that handles EOF exceptions during response writing

150

* Prevents server errors when clients disconnect early

151

*/

152

public class EofExceptionWriterInterceptor implements WriterInterceptor {

153

154

/** Intercepts response writing to handle EOF exceptions */

155

public void aroundWriteTo(WriterInterceptorContext context)

156

throws IOException, WebApplicationException;

157

}

158

```

159

160

## Custom Exception Mappers

161

162

### Creating Custom Mappers

163

164

```java

165

import io.dropwizard.jersey.errors.LoggingExceptionMapper;

166

import io.dropwizard.jersey.errors.ErrorMessage;

167

import jakarta.ws.rs.ext.Provider;

168

169

@Provider

170

public class BusinessExceptionMapper extends LoggingExceptionMapper<BusinessException> {

171

172

@Override

173

public Response toResponse(BusinessException exception) {

174

ErrorMessage error = new ErrorMessage(

175

exception.getErrorCode(),

176

exception.getMessage(),

177

exception.getDetails()

178

);

179

180

return Response

181

.status(exception.getHttpStatus())

182

.entity(error)

183

.type(MediaType.APPLICATION_JSON)

184

.build();

185

}

186

187

@Override

188

protected void logException(BusinessException exception) {

189

// Log business exceptions at WARN level

190

logger.warn("Business exception: {}", exception.getMessage(), exception);

191

}

192

}

193

194

@Provider

195

public class ValidationExceptionMapper implements ExceptionMapper<ValidationException> {

196

197

@Override

198

public Response toResponse(ValidationException exception) {

199

List<String> errors = exception.getConstraintViolations()

200

.stream()

201

.map(violation -> violation.getPropertyPath() + ": " + violation.getMessage())

202

.collect(Collectors.toList());

203

204

ErrorMessage error = new ErrorMessage(

205

400,

206

"Validation failed",

207

String.join(", ", errors)

208

);

209

210

return Response.status(400).entity(error).build();

211

}

212

}

213

```

214

215

### Security Exception Handling

216

217

```java

218

@Provider

219

public class SecurityExceptionMapper implements ExceptionMapper<SecurityException> {

220

221

@Override

222

public Response toResponse(SecurityException exception) {

223

// Don't leak sensitive information

224

ErrorMessage error = new ErrorMessage(403, "Access denied");

225

226

// Log security exceptions for monitoring

227

logger.warn("Security exception: {}", exception.getMessage());

228

229

return Response.status(403).entity(error).build();

230

}

231

}

232

233

@Provider

234

public class AuthenticationExceptionMapper implements ExceptionMapper<AuthenticationException> {

235

236

@Override

237

public Response toResponse(AuthenticationException exception) {

238

ErrorMessage error = new ErrorMessage(401, "Authentication required");

239

240

return Response.status(401)

241

.entity(error)

242

.header("WWW-Authenticate", "Bearer")

243

.build();

244

}

245

}

246

```

247

248

## Error Response Patterns

249

250

### Consistent Error Structure

251

252

All error responses follow the same JSON structure:

253

254

```json

255

{

256

"code": 400,

257

"message": "Invalid input data",

258

"details": "Field 'email' must be a valid email address"

259

}

260

```

261

262

### Error Categories

263

264

```java

265

public class ErrorCategories {

266

267

// Client errors (4xx)

268

public static ErrorMessage badRequest(String message) {

269

return new ErrorMessage(400, message);

270

}

271

272

public static ErrorMessage unauthorized(String message) {

273

return new ErrorMessage(401, message != null ? message : "Authentication required");

274

}

275

276

public static ErrorMessage forbidden(String message) {

277

return new ErrorMessage(403, message != null ? message : "Access denied");

278

}

279

280

public static ErrorMessage notFound(String resource) {

281

return new ErrorMessage(404, resource + " not found");

282

}

283

284

public static ErrorMessage conflict(String message) {

285

return new ErrorMessage(409, message);

286

}

287

288

public static ErrorMessage validationError(String details) {

289

return new ErrorMessage(422, "Validation failed", details);

290

}

291

292

// Server errors (5xx)

293

public static ErrorMessage internalError() {

294

return new ErrorMessage(500, "Internal server error");

295

}

296

297

public static ErrorMessage serviceUnavailable(String message) {

298

return new ErrorMessage(503, message != null ? message : "Service temporarily unavailable");

299

}

300

}

301

```

302

303

### Resource-Level Error Handling

304

305

```java

306

@Path("/orders")

307

public class OrderResource {

308

309

@GET

310

@Path("/{id}")

311

public Response getOrder(@PathParam("id") UUIDParam orderId) {

312

try {

313

UUID id = orderId.get(); // Parameter validation handled automatically

314

315

Order order = orderService.findById(id);

316

if (order == null) {

317

return Response.status(404)

318

.entity(new ErrorMessage(404, "Order not found"))

319

.build();

320

}

321

322

return Response.ok(order).build();

323

324

} catch (SecurityException e) {

325

return Response.status(403)

326

.entity(new ErrorMessage(403, "Access denied"))

327

.build();

328

} catch (ServiceException e) {

329

return Response.status(503)

330

.entity(new ErrorMessage(503, "Order service unavailable"))

331

.build();

332

}

333

}

334

335

@POST

336

public Response createOrder(@Valid CreateOrderRequest request) {

337

try {

338

Order order = orderService.create(request);

339

return Response.status(201)

340

.entity(order)

341

.location(URI.create("/orders/" + order.getId()))

342

.build();

343

344

} catch (InsufficientInventoryException e) {

345

return Response.status(409)

346

.entity(new ErrorMessage(409, "Insufficient inventory", e.getMessage()))

347

.build();

348

} catch (PaymentException e) {

349

return Response.status(402)

350

.entity(new ErrorMessage(402, "Payment required", e.getMessage()))

351

.build();

352

}

353

}

354

}

355

```

356

357

## Integration with Jersey

358

359

### Automatic Registration

360

361

Exception mappers are automatically registered when using DropwizardResourceConfig:

362

363

```java

364

public class MyApplication extends Application<MyConfiguration> {

365

366

@Override

367

public void run(MyConfiguration config, Environment environment) {

368

// Exception mappers are automatically registered

369

JerseyEnvironment jersey = environment.jersey();

370

371

// Register custom exception mappers

372

jersey.register(BusinessExceptionMapper.class);

373

jersey.register(ValidationExceptionMapper.class);

374

jersey.register(SecurityExceptionMapper.class);

375

}

376

}

377

```

378

379

### Error Handling Configuration

380

381

```java

382

public class ErrorHandlingConfiguration {

383

384

public void configure(JerseyEnvironment jersey) {

385

// Register error-related providers

386

jersey.register(ErrorEntityWriter.class);

387

jersey.register(EofExceptionWriterInterceptor.class);

388

389

// Configure error handling behavior

390

jersey.property(ServerProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR, true);

391

}

392

}

393

```

394

395

## Best Practices

396

397

### Exception Mapper Hierarchy

398

399

```java

400

// Generic base mapper for common exception handling

401

@Provider

402

public class BaseExceptionMapper implements ExceptionMapper<Exception> {

403

404

@Override

405

public Response toResponse(Exception exception) {

406

// Log unexpected exceptions

407

logger.error("Unexpected exception", exception);

408

409

// Return generic error message to avoid information leakage

410

ErrorMessage error = new ErrorMessage(500, "Internal server error");

411

return Response.status(500).entity(error).build();

412

}

413

414

@Priority(Priorities.USER + 1000) // Lower priority than specific mappers

415

public static class Provider {}

416

}

417

418

// Specific mappers for known exceptions

419

@Provider

420

@Priority(Priorities.USER)

421

public class SpecificExceptionMapper implements ExceptionMapper<SpecificException> {

422

// Handle specific exception types with appropriate responses

423

}

424

```

425

426

### Error Logging Strategy

427

428

```java

429

public class ErrorLoggingStrategy {

430

431

// Client errors (4xx) - log at DEBUG/INFO level

432

protected void logClientError(Exception e) {

433

logger.info("Client error: {}", e.getMessage());

434

}

435

436

// Server errors (5xx) - log at ERROR level with full stack trace

437

protected void logServerError(Exception e) {

438

logger.error("Server error", e);

439

}

440

441

// Security errors - log for monitoring

442

protected void logSecurityError(Exception e) {

443

logger.warn("Security error: {}", e.getMessage());

444

// Could integrate with security monitoring systems

445

}

446

}

447

```