0
# Resource and Exception Utilities
1
2
Utilities for resource loading with context class loader fallback and exception chain analysis tools.
3
4
## Resources Class
5
6
The `Resources` utility class provides helper methods to work with resources using appropriate class loaders.
7
8
### Resource Loading
9
10
```java { .api }
11
public static URL getResource(String resourceName);
12
```
13
14
Returns a URL pointing to the specified resource using intelligent class loader selection.
15
16
**Parameters:**
17
- `resourceName` - The name of the resource to locate
18
19
**Returns:** The URL of the resource
20
21
**Throws:**
22
- `IllegalArgumentException` - If the resource is not found
23
24
**Class Loader Selection:**
25
1. **Context Class Loader**: Uses `Thread.currentThread().getContextClassLoader()` when available
26
2. **Fallback**: Uses `Resources.class.getClassLoader()` if context class loader is `null`
27
28
This approach works correctly in various environments including app servers where different threads may have different class loaders.
29
30
### Usage Examples
31
32
#### Basic Resource Loading
33
34
```java
35
import io.dropwizard.util.Resources;
36
import java.net.URL;
37
import java.io.InputStream;
38
39
// Load a resource from the classpath
40
URL configUrl = Resources.getResource("config/application.yml");
41
URL templateUrl = Resources.getResource("templates/email-template.html");
42
URL imageUrl = Resources.getResource("static/images/logo.png");
43
44
// Convert to InputStream for reading
45
try (InputStream configStream = configUrl.openStream()) {
46
// Read configuration file
47
byte[] configBytes = configStream.readAllBytes();
48
String configContent = new String(configBytes);
49
}
50
```
51
52
#### Configuration File Loading
53
54
```java
55
public class ConfigurationLoader {
56
public Properties loadProperties(String resourceName) {
57
try {
58
URL resourceUrl = Resources.getResource(resourceName);
59
Properties props = new Properties();
60
try (InputStream input = resourceUrl.openStream()) {
61
props.load(input);
62
}
63
return props;
64
} catch (IllegalArgumentException e) {
65
throw new RuntimeException("Configuration file not found: " + resourceName, e);
66
} catch (IOException e) {
67
throw new RuntimeException("Failed to load configuration: " + resourceName, e);
68
}
69
}
70
}
71
72
// Usage
73
ConfigurationLoader loader = new ConfigurationLoader();
74
Properties appProps = loader.loadProperties("application.properties");
75
Properties dbProps = loader.loadProperties("database.properties");
76
```
77
78
#### Template and Asset Loading
79
80
```java
81
public class TemplateService {
82
public String loadTemplate(String templateName) {
83
try {
84
URL templateUrl = Resources.getResource("templates/" + templateName);
85
try (InputStream input = templateUrl.openStream()) {
86
return new String(input.readAllBytes(), StandardCharsets.UTF_8);
87
}
88
} catch (IllegalArgumentException e) {
89
throw new RuntimeException("Template not found: " + templateName, e);
90
} catch (IOException e) {
91
throw new RuntimeException("Failed to read template: " + templateName, e);
92
}
93
}
94
}
95
96
// Usage
97
TemplateService templateService = new TemplateService();
98
String emailTemplate = templateService.loadTemplate("email-notification.html");
99
String reportTemplate = templateService.loadTemplate("monthly-report.html");
100
```
101
102
#### Web Application Context
103
104
```java
105
// In servlet or web application contexts where context class loader
106
// is set appropriately for resource discovery
107
public class WebResourceLoader {
108
public byte[] loadWebAsset(String assetPath) {
109
try {
110
// Resources.getResource will use the context class loader
111
// which is typically set correctly in web applications
112
URL assetUrl = Resources.getResource("static/" + assetPath);
113
try (InputStream input = assetUrl.openStream()) {
114
return input.readAllBytes();
115
}
116
} catch (IllegalArgumentException e) {
117
throw new RuntimeException("Web asset not found: " + assetPath, e);
118
} catch (IOException e) {
119
throw new RuntimeException("Failed to load web asset: " + assetPath, e);
120
}
121
}
122
}
123
```
124
125
## Throwables Class
126
127
The `Throwables` utility class provides helper methods to work with exception objects and exception chains.
128
129
### Exception Chain Search
130
131
```java { .api }
132
public static Optional<Throwable> findThrowableInChain(
133
Predicate<Throwable> condition,
134
Throwable t
135
);
136
```
137
138
Search an exception chain for an exception matching a given condition.
139
140
**Parameters:**
141
- `condition` - The condition predicate to match against each exception in the chain
142
- `t` - The head of the exception chain (can be `null`)
143
144
**Returns:** An `Optional` containing the first matching exception in the chain, or empty if no match found
145
146
**Search Behavior:**
147
- Starts from the head exception and traverses the chain using `getCause()`
148
- Prevents infinite loops by tracking visited exceptions
149
- Returns the first exception that matches the condition
150
- Returns empty `Optional` if no match is found or if the input throwable is `null`
151
152
### Usage Examples
153
154
#### Find Specific Exception Types
155
156
```java
157
import io.dropwizard.util.Throwables;
158
import java.util.Optional;
159
160
// Example exception chain
161
Exception rootCause = new SQLException("Connection failed");
162
Exception cause = new RuntimeException("Database error", rootCause);
163
Exception topLevel = new ServiceException("Service unavailable", cause);
164
165
// Find SQLException in the chain
166
Optional<Throwable> sqlEx = Throwables.findThrowableInChain(
167
ex -> ex instanceof SQLException,
168
topLevel
169
);
170
171
if (sqlEx.isPresent()) {
172
SQLException sql = (SQLException) sqlEx.get();
173
System.out.println("Found SQL error: " + sql.getMessage());
174
}
175
176
// Find by exception message
177
Optional<Throwable> connectionError = Throwables.findThrowableInChain(
178
ex -> ex.getMessage() != null && ex.getMessage().contains("Connection"),
179
topLevel
180
);
181
```
182
183
#### Database Exception Analysis
184
185
```java
186
public class DatabaseService {
187
public void handleDatabaseOperation() {
188
try {
189
performDatabaseOperation();
190
} catch (Exception e) {
191
analyzeDatabaseException(e);
192
throw e;
193
}
194
}
195
196
private void analyzeDatabaseException(Exception e) {
197
// Look for specific database errors
198
Optional<Throwable> sqlEx = Throwables.findThrowableInChain(
199
ex -> ex instanceof SQLException,
200
e
201
);
202
203
if (sqlEx.isPresent()) {
204
SQLException sql = (SQLException) sqlEx.get();
205
logDatabaseError(sql.getErrorCode(), sql.getSQLState(), sql.getMessage());
206
}
207
208
// Look for connection-related errors
209
Optional<Throwable> connectionEx = Throwables.findThrowableInChain(
210
ex -> ex.getMessage() != null &&
211
(ex.getMessage().contains("connection") ||
212
ex.getMessage().contains("Connection")),
213
e
214
);
215
216
if (connectionEx.isPresent()) {
217
alertConnectionIssue(connectionEx.get().getMessage());
218
}
219
}
220
}
221
```
222
223
#### Retry Logic Based on Exception Analysis
224
225
```java
226
public class RetryableService {
227
public void performWithRetry() {
228
int maxRetries = 3;
229
int attempt = 0;
230
231
while (attempt < maxRetries) {
232
try {
233
performOperation();
234
return; // Success
235
} catch (Exception e) {
236
if (shouldRetry(e)) {
237
attempt++;
238
if (attempt >= maxRetries) {
239
throw new RuntimeException("Max retries exceeded", e);
240
}
241
try {
242
Thread.sleep(1000 * attempt); // Exponential backoff
243
} catch (InterruptedException ie) {
244
Thread.currentThread().interrupt();
245
throw new RuntimeException("Interrupted during retry", ie);
246
}
247
} else {
248
throw e; // Don't retry
249
}
250
}
251
}
252
}
253
254
private boolean shouldRetry(Exception e) {
255
// Retry on temporary network issues
256
Optional<Throwable> networkError = Throwables.findThrowableInChain(
257
ex -> ex instanceof SocketTimeoutException ||
258
ex instanceof ConnectException ||
259
(ex.getMessage() != null && ex.getMessage().contains("timeout")),
260
e
261
);
262
263
if (networkError.isPresent()) {
264
return true;
265
}
266
267
// Don't retry on authentication/authorization issues
268
Optional<Throwable> authError = Throwables.findThrowableInChain(
269
ex -> ex instanceof SecurityException ||
270
(ex.getMessage() != null &&
271
(ex.getMessage().contains("authentication") ||
272
ex.getMessage().contains("authorization"))),
273
e
274
);
275
276
return authError.isEmpty(); // Retry only if no auth error found
277
}
278
}
279
```
280
281
#### Exception Logging and Monitoring
282
283
```java
284
public class ExceptionAnalyzer {
285
private static final Logger logger = LoggerFactory.getLogger(ExceptionAnalyzer.class);
286
287
public void analyzeAndLog(Exception e, String operation) {
288
// Log the top-level exception
289
logger.error("Operation failed: {}", operation, e);
290
291
// Find and log specific error types with additional context
292
findAndLogSpecificErrors(e);
293
294
// Extract metrics for monitoring
295
extractMetricsFromException(e, operation);
296
}
297
298
private void findAndLogSpecificErrors(Exception e) {
299
// Look for timeout issues
300
Optional<Throwable> timeout = Throwables.findThrowableInChain(
301
ex -> ex instanceof TimeoutException ||
302
(ex.getMessage() != null && ex.getMessage().contains("timeout")),
303
e
304
);
305
timeout.ifPresent(ex -> logger.warn("Timeout detected: {}", ex.getMessage()));
306
307
// Look for resource issues
308
Optional<Throwable> resource = Throwables.findThrowableInChain(
309
ex -> ex instanceof OutOfMemoryError ||
310
ex instanceof IOException,
311
e
312
);
313
resource.ifPresent(ex -> logger.error("Resource issue: {}", ex.getMessage()));
314
315
// Look for configuration issues
316
Optional<Throwable> config = Throwables.findThrowableInChain(
317
ex -> ex instanceof IllegalArgumentException ||
318
ex instanceof IllegalStateException,
319
e
320
);
321
config.ifPresent(ex -> logger.warn("Configuration issue: {}", ex.getMessage()));
322
}
323
324
private void extractMetricsFromException(Exception e, String operation) {
325
// Count different types of errors for monitoring dashboards
326
Throwables.findThrowableInChain(ex -> ex instanceof SQLException, e)
327
.ifPresent(ex -> incrementMetric("database.errors", operation));
328
329
Throwables.findThrowableInChain(ex -> ex instanceof SocketTimeoutException, e)
330
.ifPresent(ex -> incrementMetric("network.timeouts", operation));
331
332
Throwables.findThrowableInChain(ex -> ex instanceof SecurityException, e)
333
.ifPresent(ex -> incrementMetric("security.errors", operation));
334
}
335
}
336
```
337
338
## Error Handling
339
340
### Resources Error Handling
341
342
```java
343
try {
344
URL resource = Resources.getResource("missing-file.txt");
345
} catch (IllegalArgumentException e) {
346
// Resource not found
347
logger.warn("Resource not found: {}", e.getMessage());
348
// Provide fallback or default behavior
349
}
350
```
351
352
### Throwables Error Handling
353
354
```java
355
// findThrowableInChain handles null input gracefully
356
Exception nullException = null;
357
Optional<Throwable> result = Throwables.findThrowableInChain(
358
ex -> ex instanceof IOException,
359
nullException
360
);
361
// result will be Optional.empty(), no exception thrown
362
363
// Handles circular reference detection automatically
364
Exception circular1 = new RuntimeException("Error 1");
365
Exception circular2 = new RuntimeException("Error 2", circular1);
366
// Don't do this in real code, but if it happens, the method handles it
367
// circular1.initCause(circular2); // Would create circular reference
368
369
Optional<Throwable> result2 = Throwables.findThrowableInChain(
370
ex -> ex.getMessage().contains("Error"),
371
circular1
372
);
373
// Method prevents infinite loops by tracking visited exceptions
374
```
375
376
## Integration Patterns
377
378
### Configuration Loading with Fallbacks
379
380
```java
381
public class ConfigurationManager {
382
public Properties loadConfiguration(String primaryResource, String fallbackResource) {
383
Properties props = new Properties();
384
385
// Try primary resource first
386
try {
387
URL primaryUrl = Resources.getResource(primaryResource);
388
try (InputStream input = primaryUrl.openStream()) {
389
props.load(input);
390
return props;
391
}
392
} catch (IllegalArgumentException e) {
393
logger.info("Primary config not found: {}, trying fallback", primaryResource);
394
} catch (IOException e) {
395
logger.warn("Failed to load primary config: {}", primaryResource, e);
396
}
397
398
// Fallback to secondary resource
399
try {
400
URL fallbackUrl = Resources.getResource(fallbackResource);
401
try (InputStream input = fallbackUrl.openStream()) {
402
props.load(input);
403
return props;
404
}
405
} catch (Exception e) {
406
throw new RuntimeException("Cannot load configuration from either primary or fallback resource", e);
407
}
408
}
409
}
410
```