or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

default-annotations.mdindex.mdnull-safety.mdresource-management.mdreturn-value-checking.mdtesting-annotations.mdwarning-suppression.md

resource-management.mddocs/

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