0
# Resource Management
1
2
Track resource creation, cleanup obligations, and lifecycle management for preventing resource leaks. These annotations work with experimental SpotBugs detectors to ensure proper resource handling patterns.
3
4
## Capabilities
5
6
### CleanupObligation Annotation
7
8
Mark a class or interface as a resource type requiring cleanup.
9
10
```java { .api }
11
/**
12
* Mark a class or interface as a resource type requiring cleanup.
13
*/
14
@Documented
15
@Retention(RetentionPolicy.RUNTIME)
16
@Target(ElementType.TYPE)
17
@interface CleanupObligation {
18
}
19
```
20
21
**Usage Examples:**
22
23
```java
24
// Mark classes that require cleanup
25
@CleanupObligation
26
public class DatabaseConnection {
27
private Connection connection;
28
private boolean closed = false;
29
30
public DatabaseConnection(String url) throws SQLException {
31
this.connection = DriverManager.getConnection(url);
32
}
33
34
// Methods that use the resource
35
public ResultSet executeQuery(String sql) throws SQLException {
36
if (closed) {
37
throw new IllegalStateException("Connection is closed");
38
}
39
return connection.createStatement().executeQuery(sql);
40
}
41
42
// Cleanup method that discharges the obligation
43
@DischargesObligation
44
public void close() throws SQLException {
45
if (!closed) {
46
connection.close();
47
closed = true;
48
}
49
}
50
}
51
52
@CleanupObligation
53
public interface ManagedResource {
54
void use();
55
56
@DischargesObligation
57
void cleanup();
58
}
59
60
@CleanupObligation
61
public class FileHandle implements AutoCloseable {
62
private FileInputStream stream;
63
64
public FileHandle(String filename) throws IOException {
65
this.stream = new FileInputStream(filename);
66
}
67
68
public int read() throws IOException {
69
return stream.read();
70
}
71
72
@Override
73
@DischargesObligation
74
public void close() throws IOException {
75
if (stream != null) {
76
stream.close();
77
stream = null;
78
}
79
}
80
}
81
```
82
83
### CreatesObligation Annotation
84
85
Mark a constructor or method as creating a resource which requires cleanup.
86
87
```java { .api }
88
/**
89
* Mark a constructor or method as creating a resource which requires cleanup.
90
* The marked method must be a member of a class marked with the
91
* CleanupObligation annotation.
92
*/
93
@Documented
94
@Retention(RetentionPolicy.RUNTIME)
95
@Target({ ElementType.METHOD, ElementType.CONSTRUCTOR })
96
@interface CreatesObligation {
97
}
98
```
99
100
**Usage Examples:**
101
102
```java
103
@CleanupObligation
104
public class ResourceManager {
105
private List<Resource> resources = new ArrayList<>();
106
107
// Constructor creates obligation
108
@CreatesObligation
109
public ResourceManager() {
110
// Initialize resource management
111
initializeCleanupHooks();
112
}
113
114
// Factory method creates obligation
115
@CreatesObligation
116
public static ResourceManager create(String config) {
117
ResourceManager manager = new ResourceManager();
118
manager.configure(config);
119
return manager; // Caller must ensure cleanup
120
}
121
122
// Method that creates obligation
123
@CreatesObligation
124
public Resource acquireResource(String resourceId) {
125
Resource resource = resourcePool.acquire(resourceId);
126
resources.add(resource);
127
return resource; // Creates cleanup obligation
128
}
129
130
@DischargesObligation
131
public void shutdown() {
132
for (Resource resource : resources) {
133
resource.release();
134
}
135
resources.clear();
136
}
137
}
138
139
// Usage patterns
140
public class ResourceClient {
141
142
public void useResources() {
143
// Creating obligation - must ensure cleanup
144
ResourceManager manager = new ResourceManager(); // @CreatesObligation
145
146
try {
147
Resource resource = manager.acquireResource("db-pool"); // @CreatesObligation
148
149
// Use resources
150
resource.performOperation();
151
152
} finally {
153
// Discharge obligation
154
manager.shutdown(); // @DischargesObligation
155
}
156
}
157
158
public void useWithTryWithResources() {
159
// Better pattern with AutoCloseable
160
try (ResourceManager manager = ResourceManager.create("config")) {
161
Resource resource = manager.acquireResource("cache");
162
resource.performOperation();
163
} // Automatic cleanup
164
}
165
}
166
```
167
168
### DischargesObligation Annotation
169
170
Mark a method as cleaning up a resource and discharging the cleanup obligation.
171
172
```java { .api }
173
/**
174
* Mark a method as cleaning up a resource. The marked method must be a member
175
* of a class marked with the CleanupObligation annotation.
176
*/
177
@Documented
178
@Retention(RetentionPolicy.RUNTIME)
179
@Target(ElementType.METHOD)
180
@interface DischargesObligation {
181
}
182
```
183
184
**Usage Examples:**
185
186
```java
187
@CleanupObligation
188
public class NetworkConnection {
189
private Socket socket;
190
private boolean connected = false;
191
192
@CreatesObligation
193
public NetworkConnection(String host, int port) throws IOException {
194
this.socket = new Socket(host, port);
195
this.connected = true;
196
}
197
198
public void sendData(byte[] data) throws IOException {
199
if (!connected) {
200
throw new IllegalStateException("Connection closed");
201
}
202
socket.getOutputStream().write(data);
203
}
204
205
// Primary cleanup method
206
@DischargesObligation
207
public void disconnect() throws IOException {
208
if (connected) {
209
socket.close();
210
connected = false;
211
}
212
}
213
214
// Alternative cleanup method
215
@DischargesObligation
216
public void forceClose() {
217
if (connected) {
218
try {
219
socket.close();
220
} catch (IOException e) {
221
// Log but don't throw - this is force close
222
logger.warn("Error during force close", e);
223
}
224
connected = false;
225
}
226
}
227
228
// Finalizer as last resort (not recommended but shows pattern)
229
@DischargesObligation
230
@Override
231
protected void finalize() throws Throwable {
232
try {
233
if (connected) {
234
forceClose();
235
}
236
} finally {
237
super.finalize();
238
}
239
}
240
}
241
242
// Multiple cleanup methods example
243
@CleanupObligation
244
public class DatabaseTransaction {
245
private Connection connection;
246
private boolean active = true;
247
248
@CreatesObligation
249
public DatabaseTransaction(Connection connection) {
250
this.connection = connection;
251
try {
252
connection.setAutoCommit(false);
253
} catch (SQLException e) {
254
throw new RuntimeException("Failed to start transaction", e);
255
}
256
}
257
258
// Successful completion discharges obligation
259
@DischargesObligation
260
public void commit() throws SQLException {
261
if (active) {
262
connection.commit();
263
connection.setAutoCommit(true);
264
active = false;
265
}
266
}
267
268
// Unsuccessful completion also discharges obligation
269
@DischargesObligation
270
public void rollback() throws SQLException {
271
if (active) {
272
connection.rollback();
273
connection.setAutoCommit(true);
274
active = false;
275
}
276
}
277
}
278
```
279
280
### OverrideMustInvoke Annotation
281
282
Indicate that an overriding method must invoke the overridden method.
283
284
```java { .api }
285
/**
286
* Indicate that an overriding method must invoke the overridden method.
287
*/
288
@Documented
289
@Target(ElementType.METHOD)
290
@Retention(RetentionPolicy.CLASS)
291
@interface OverrideMustInvoke {
292
/**
293
* When the superclass method should be invoked
294
*/
295
When value() default When.ANYTIME;
296
}
297
```
298
299
**Usage Examples:**
300
301
```java
302
public abstract class BaseResource {
303
304
// Subclasses must call super.initialize()
305
@OverrideMustInvoke(When.FIRST)
306
protected void initialize() {
307
// Base initialization that must happen first
308
setupLogging();
309
validateConfiguration();
310
}
311
312
// Subclasses must call super.cleanup()
313
@OverrideMustInvoke(When.LAST)
314
protected void cleanup() {
315
// Base cleanup that must happen last
316
closeConnections();
317
releaseResources();
318
}
319
320
// Subclasses must call super method at some point
321
@OverrideMustInvoke(When.ANYTIME)
322
protected void processData(String data) {
323
// Common processing logic
324
validateData(data);
325
logProcessing(data);
326
}
327
}
328
329
public class DatabaseResource extends BaseResource {
330
331
@Override
332
protected void initialize() {
333
// MUST call super.initialize() first due to When.FIRST
334
super.initialize();
335
336
// Subclass-specific initialization
337
connectToDatabase();
338
createTables();
339
}
340
341
@Override
342
protected void cleanup() {
343
// Subclass-specific cleanup first
344
closeDatabaseConnections();
345
dropTemporaryTables();
346
347
// MUST call super.cleanup() last due to When.LAST
348
super.cleanup();
349
}
350
351
@Override
352
protected void processData(String data) {
353
// Can call super method at any point (When.ANYTIME)
354
preprocessData(data);
355
super.processData(data); // Call at any point
356
postprocessData(data);
357
}
358
}
359
360
// Usage with resource management
361
@CleanupObligation
362
public abstract class ManagedService {
363
364
@CreatesObligation
365
public ManagedService() {
366
initialize();
367
}
368
369
@OverrideMustInvoke(When.FIRST)
370
protected void initialize() {
371
// Base service initialization
372
startMetrics();
373
registerShutdownHook();
374
}
375
376
@OverrideMustInvoke(When.LAST)
377
@DischargesObligation
378
public void shutdown() {
379
// Base service shutdown
380
stopMetrics();
381
unregisterShutdownHook();
382
}
383
}
384
```
385
386
### When Enum (Deprecated)
387
388
Specifies when a method should be invoked during override.
389
390
```java { .api }
391
/**
392
* @deprecated Legacy enum for method invocation timing
393
*/
394
@Deprecated
395
enum When {
396
/** Method should be invoked first, before subclass logic */
397
FIRST,
398
399
/** Method can be invoked at any time during subclass execution */
400
ANYTIME,
401
402
/** Method should be invoked last, after subclass logic */
403
LAST
404
}
405
```
406
407
## Resource Management Patterns
408
409
### Try-With-Resources Integration
410
411
```java
412
@CleanupObligation
413
public class AutoCloseableResource implements AutoCloseable {
414
415
@CreatesObligation
416
public AutoCloseableResource(String resourceId) {
417
// Acquire resource
418
}
419
420
@Override
421
@DischargesObligation
422
public void close() {
423
// Release resource
424
}
425
}
426
427
// Usage with automatic cleanup
428
public void processWithAutoClose() {
429
try (AutoCloseableResource resource = new AutoCloseableResource("id")) {
430
resource.performOperation();
431
} // Automatic cleanup via close()
432
}
433
```
434
435
### Resource Pools
436
437
```java
438
@CleanupObligation
439
public class ConnectionPool {
440
441
@CreatesObligation
442
public static ConnectionPool create(int maxConnections) {
443
return new ConnectionPool(maxConnections);
444
}
445
446
@CreatesObligation
447
public Connection borrowConnection() {
448
return connectionQueue.take(); // Creates obligation to return
449
}
450
451
@DischargesObligation
452
public void returnConnection(Connection connection) {
453
connectionQueue.offer(connection); // Discharges borrow obligation
454
}
455
456
@DischargesObligation
457
public void shutdown() {
458
// Close all pooled connections
459
connectionQueue.forEach(Connection::close);
460
}
461
}
462
```
463
464
## Best Practices
465
466
1. **Mark resource types**: Apply @CleanupObligation to all classes that manage resources
467
2. **Mark creation points**: Use @CreatesObligation on constructors and factory methods
468
3. **Mark cleanup methods**: Use @DischargesObligation on all methods that release resources
469
4. **Integrate with AutoCloseable**: Implement AutoCloseable when possible for automatic cleanup
470
5. **Use try-with-resources**: Prefer try-with-resources for automatic resource management
471
6. **Handle exceptions**: Ensure cleanup methods handle exceptions gracefully
472
7. **Document lifecycle**: Clearly document the expected resource lifecycle
473
8. **Use override annotations**: Apply @OverrideMustInvoke to ensure proper inheritance patterns