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

http-caching.mddocs/

0

# HTTP Caching

1

2

HTTP caching support through annotations with comprehensive Cache-Control header configuration for optimizing web service performance. Provides declarative caching configuration with flexible cache control directives.

3

4

## Capabilities

5

6

### CacheControl Annotation

7

8

Comprehensive annotation for configuring HTTP Cache-Control headers with support for all standard cache directives.

9

10

```java { .api }

11

/**

12

* Annotation for adding Cache-Control headers to HTTP responses

13

* Supports all standard HTTP cache control directives

14

*/

15

@Documented

16

@Target(ElementType.METHOD)

17

@Retention(RetentionPolicy.RUNTIME)

18

public @interface CacheControl {

19

20

/**

21

* Marks response as immutable for maximum caching (1 year max-age)

22

* @return true if response should be cached indefinitely

23

*/

24

boolean immutable() default false;

25

26

/**

27

* Controls private cache directive - response only cacheable by private caches

28

* @return true if response must not be stored by shared caches

29

*/

30

boolean isPrivate() default false;

31

32

/**

33

* Controls no-cache directive - response must be revalidated before use

34

* @return true if response must not be used without revalidation

35

*/

36

boolean noCache() default false;

37

38

/**

39

* Controls no-store directive - response must not be stored at all

40

* @return true if response must not be stored in any cache

41

*/

42

boolean noStore() default false;

43

44

/**

45

* Controls no-transform directive - intermediaries must not modify response

46

* @return true if response must not be transformed by intermediaries

47

*/

48

boolean noTransform() default true;

49

50

/**

51

* Controls must-revalidate directive - stale responses require revalidation

52

* @return true if caches must revalidate when response becomes stale

53

*/

54

boolean mustRevalidate() default false;

55

56

/**

57

* Controls proxy-revalidate directive - like must-revalidate but only for shared caches

58

* @return true if only proxies must revalidate when response becomes stale

59

*/

60

boolean proxyRevalidate() default false;

61

62

/**

63

* Sets max-age directive value in specified time units

64

* @return maximum age for response freshness (-1 to disable)

65

*/

66

int maxAge() default -1;

67

68

/**

69

* Time unit for max-age value

70

* @return time unit for maxAge value

71

*/

72

TimeUnit maxAgeUnit() default TimeUnit.SECONDS;

73

74

/**

75

* Sets stale-while-revalidate directive for serving stale content during revalidation

76

* @return time period for serving stale content while revalidating (-1 to disable)

77

*/

78

int staleWhileRevalidate() default -1;

79

80

/**

81

* Time unit for stale-while-revalidate value

82

* @return time unit for staleWhileRevalidate value

83

*/

84

TimeUnit staleWhileRevalidateUnit() default TimeUnit.SECONDS;

85

86

/**

87

* Sets s-max-age directive for shared cache maximum age

88

* @return maximum age for shared caches (-1 to disable)

89

*/

90

int sharedMaxAge() default -1;

91

92

/**

93

* Time unit for s-max-age value

94

* @return time unit for sharedMaxAge value

95

*/

96

TimeUnit sharedMaxAgeUnit() default TimeUnit.SECONDS;

97

}

98

```

99

100

**Usage Examples:**

101

102

```java

103

import io.dropwizard.jersey.caching.CacheControl;

104

import jakarta.ws.rs.*;

105

import java.util.concurrent.TimeUnit;

106

107

@Path("/api")

108

public class CacheableResource {

109

110

// Basic caching - 5 minutes

111

@GET

112

@Path("/data")

113

@CacheControl(maxAge = 5, maxAgeUnit = TimeUnit.MINUTES)

114

public Data getData() {

115

return dataService.getCurrentData();

116

}

117

118

// Immutable content - cached indefinitely

119

@GET

120

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

121

@CacheControl(immutable = true)

122

public StaticContent getStaticContent(@PathParam("id") String id) {

123

return staticContentService.getById(id);

124

}

125

126

// Private caching only - not cached by proxies

127

@GET

128

@Path("/user/private")

129

@CacheControl(isPrivate = true, maxAge = 30, maxAgeUnit = TimeUnit.MINUTES)

130

public UserData getPrivateUserData() {

131

return userService.getPrivateData();

132

}

133

134

// No caching - always revalidate

135

@GET

136

@Path("/realtime")

137

@CacheControl(noCache = true)

138

public RealtimeData getRealtimeData() {

139

return realtimeService.getCurrentData();

140

}

141

142

// Never store in cache

143

@GET

144

@Path("/sensitive")

145

@CacheControl(noStore = true)

146

public SensitiveData getSensitiveData() {

147

return sensitiveService.getData();

148

}

149

150

// Complex caching with multiple directives

151

@GET

152

@Path("/complex")

153

@CacheControl(

154

maxAge = 1, maxAgeUnit = TimeUnit.HOURS,

155

sharedMaxAge = 30, sharedMaxAgeUnit = TimeUnit.MINUTES,

156

mustRevalidate = true,

157

staleWhileRevalidate = 5, staleWhileRevalidateUnit = TimeUnit.MINUTES

158

)

159

public ComplexData getComplexData() {

160

return complexService.getData();

161

}

162

}

163

```

164

165

### CacheControlledResponseFeature

166

167

Jersey feature that processes @CacheControl annotations and adds appropriate headers to responses.

168

169

```java { .api }

170

/**

171

* Jersey feature that enables @CacheControl annotation processing

172

* Automatically registered by DropwizardResourceConfig

173

*/

174

public class CacheControlledResponseFeature implements Feature {

175

176

/**

177

* Configures the feature with Jersey

178

* @param context Jersey feature context

179

* @return true if feature was successfully configured

180

*/

181

public boolean configure(FeatureContext context);

182

}

183

```

184

185

## Cache Control Strategies

186

187

### Public Content Caching

188

189

```java

190

@Path("/public")

191

public class PublicContentResource {

192

193

// Static assets - cache for 1 year

194

@GET

195

@Path("/assets/{filename}")

196

@CacheControl(immutable = true)

197

public Response getAsset(@PathParam("filename") String filename) {

198

byte[] content = assetService.getAsset(filename);

199

return Response.ok(content)

200

.type(getContentType(filename))

201

.build();

202

}

203

204

// API data - cache for 1 hour

205

@GET

206

@Path("/catalog")

207

@CacheControl(maxAge = 1, maxAgeUnit = TimeUnit.HOURS)

208

public ProductCatalog getCatalog() {

209

return catalogService.getPublicCatalog();

210

}

211

212

// Frequently updated content - cache for 5 minutes

213

@GET

214

@Path("/news")

215

@CacheControl(maxAge = 5, maxAgeUnit = TimeUnit.MINUTES)

216

public List<NewsItem> getNews() {

217

return newsService.getLatestNews();

218

}

219

220

// CDN optimization - different cache times for different levels

221

@GET

222

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

223

@CacheControl(

224

maxAge = 7, maxAgeUnit = TimeUnit.DAYS, // Browser cache: 7 days

225

sharedMaxAge = 30, sharedMaxAgeUnit = TimeUnit.DAYS // CDN cache: 30 days

226

)

227

public Response getImage(@PathParam("id") String imageId) {

228

byte[] image = imageService.getImage(imageId);

229

return Response.ok(image).type("image/jpeg").build();

230

}

231

}

232

```

233

234

### Private Content Caching

235

236

```java

237

@Path("/user")

238

public class UserContentResource {

239

240

// User-specific data - private cache only

241

@GET

242

@Path("/profile")

243

@CacheControl(isPrivate = true, maxAge = 15, maxAgeUnit = TimeUnit.MINUTES)

244

public UserProfile getUserProfile(@Context SecurityContext security) {

245

String userId = security.getUserPrincipal().getName();

246

return userService.getProfile(userId);

247

}

248

249

// User preferences - private, short cache

250

@GET

251

@Path("/preferences")

252

@CacheControl(isPrivate = true, maxAge = 5, maxAgeUnit = TimeUnit.MINUTES)

253

public UserPreferences getUserPreferences(@Context SecurityContext security) {

254

String userId = security.getUserPrincipal().getName();

255

return userService.getPreferences(userId);

256

}

257

258

// Sensitive data - no caching at all

259

@GET

260

@Path("/financial")

261

@CacheControl(noStore = true)

262

public FinancialData getFinancialData(@Context SecurityContext security) {

263

String userId = security.getUserPrincipal().getName();

264

return financialService.getData(userId);

265

}

266

}

267

```

268

269

### Dynamic Content Caching

270

271

```java

272

@Path("/dynamic")

273

public class DynamicContentResource {

274

275

// Content that changes frequently - must revalidate

276

@GET

277

@Path("/status")

278

@CacheControl(

279

maxAge = 30, maxAgeUnit = TimeUnit.SECONDS,

280

mustRevalidate = true

281

)

282

public SystemStatus getSystemStatus() {

283

return statusService.getCurrentStatus();

284

}

285

286

// Content with ETags for conditional requests

287

@GET

288

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

289

@CacheControl(maxAge = 10, maxAgeUnit = TimeUnit.MINUTES)

290

public Response getData(@PathParam("id") String id,

291

@Context Request request) {

292

293

Data data = dataService.getById(id);

294

EntityTag etag = new EntityTag(data.getVersion());

295

296

// Check if client has current version

297

Response.ResponseBuilder builder = request.evaluatePreconditions(etag);

298

if (builder != null) {

299

return builder.build(); // 304 Not Modified

300

}

301

302

return Response.ok(data).tag(etag).build();

303

}

304

305

// Stale-while-revalidate for better performance

306

@GET

307

@Path("/expensive")

308

@CacheControl(

309

maxAge = 5, maxAgeUnit = TimeUnit.MINUTES,

310

staleWhileRevalidate = 1, staleWhileRevalidateUnit = TimeUnit.HOURS

311

)

312

public ExpensiveData getExpensiveData() {

313

// Allow serving stale content for 1 hour while revalidating

314

return expensiveService.computeData();

315

}

316

}

317

```

318

319

### Conditional Caching

320

321

```java

322

@Path("/conditional")

323

public class ConditionalCacheResource {

324

325

@GET

326

@Path("/data")

327

public Response getDataWithConditionalCaching(@QueryParam("format") String format) {

328

329

if ("json".equals(format)) {

330

// JSON format - cache for 1 hour

331

Data data = dataService.getData();

332

return Response.ok(data)

333

.header("Cache-Control", "max-age=3600")

334

.build();

335

336

} else if ("xml".equals(format)) {

337

// XML format - cache for 30 minutes

338

Data data = dataService.getData();

339

String xml = xmlService.toXml(data);

340

return Response.ok(xml)

341

.type(MediaType.APPLICATION_XML)

342

.header("Cache-Control", "max-age=1800")

343

.build();

344

345

} else {

346

// Unknown format - no caching

347

return Response.status(400)

348

.entity("Unsupported format")

349

.header("Cache-Control", "no-store")

350

.build();

351

}

352

}

353

354

// Method-level caching with runtime conditions

355

@GET

356

@Path("/contextual/{type}")

357

@CacheControl(maxAge = 1, maxAgeUnit = TimeUnit.HOURS) // Default caching

358

public Response getContextualData(@PathParam("type") String type,

359

@Context HttpHeaders headers) {

360

361

ContextualData data = contextService.getData(type);

362

363

// Override caching based on content type

364

ResponseBuilder builder = Response.ok(data);

365

366

if (data.isHighlyDynamic()) {

367

// Override annotation for dynamic content

368

builder.header("Cache-Control", "no-cache, must-revalidate");

369

} else if (data.isStatic()) {

370

// Extend caching for static content

371

builder.header("Cache-Control", "public, max-age=86400, immutable");

372

}

373

// Otherwise use annotation defaults

374

375

return builder.build();

376

}

377

}

378

```

379

380

## Advanced Caching Patterns

381

382

### Cache Invalidation

383

384

```java

385

@Path("/admin")

386

public class CacheInvalidationResource {

387

388

@POST

389

@Path("/cache/invalidate")

390

public Response invalidateCache(@QueryParam("pattern") String pattern) {

391

// Trigger cache invalidation

392

cacheService.invalidate(pattern);

393

394

return Response.ok()

395

.header("Cache-Control", "no-cache")

396

.entity("Cache invalidated")

397

.build();

398

}

399

400

@PUT

401

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

402

public Response updateData(@PathParam("id") String id, Data data) {

403

Data updated = dataService.update(id, data);

404

405

// Set cache headers for updated resource

406

return Response.ok(updated)

407

.header("Cache-Control", "max-age=300, must-revalidate")

408

.header("ETag", "\"" + updated.getVersion() + "\"")

409

.build();

410

}

411

}

412

```

413

414

### Multi-tier Caching

415

416

```java

417

@Path("/tiered")

418

public class MultiTierCacheResource {

419

420

// Different cache durations for different tiers

421

@GET

422

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

423

@CacheControl(

424

maxAge = 5, maxAgeUnit = TimeUnit.MINUTES, // Browser: 5 minutes

425

sharedMaxAge = 1, sharedMaxAgeUnit = TimeUnit.HOURS, // CDN: 1 hour

426

staleWhileRevalidate = 10, staleWhileRevalidateUnit = TimeUnit.MINUTES

427

)

428

public ContentItem getContent(@PathParam("id") String id) {

429

return contentService.getById(id);

430

}

431

432

// Geographic caching considerations

433

@GET

434

@Path("/localized/{region}")

435

@CacheControl(

436

isPrivate = false,

437

maxAge = 30, maxAgeUnit = TimeUnit.MINUTES,

438

sharedMaxAge = 2, sharedMaxAgeUnit = TimeUnit.HOURS

439

)

440

public LocalizedContent getLocalizedContent(@PathParam("region") String region) {

441

return contentService.getLocalizedContent(region);

442

}

443

}

444

```

445

446

## Configuration and Best Practices

447

448

### Cache Configuration

449

450

```java

451

public class CacheConfiguration {

452

453

public void configureCaching(JerseyEnvironment jersey) {

454

// CacheControlledResponseFeature is automatically registered

455

// by DropwizardResourceConfig

456

457

// Additional cache-related configuration

458

jersey.property(ServerProperties.OUTBOUND_CONTENT_LENGTH_BUFFER, 8192);

459

}

460

}

461

```

462

463

### Cache Header Best Practices

464

465

```java

466

public class CacheBestPractices {

467

468

// Static assets - use immutable with versioning

469

@GET

470

@Path("/static/v{version}/{file}")

471

@CacheControl(immutable = true)

472

public Response getVersionedAsset(@PathParam("version") String version,

473

@PathParam("file") String file) {

474

// Immutable because URL changes when content changes

475

return Response.ok(assetService.getAsset(version, file)).build();

476

}

477

478

// API responses - balance freshness with performance

479

@GET

480

@Path("/api/users")

481

@CacheControl(

482

maxAge = 2, maxAgeUnit = TimeUnit.MINUTES, // Fresh for 2 minutes

483

staleWhileRevalidate = 10, staleWhileRevalidateUnit = TimeUnit.MINUTES // Serve stale for 10 minutes while updating

484

)

485

public List<User> getUsers() {

486

return userService.getAllUsers();

487

}

488

489

// Personalized content - private caching only

490

@GET

491

@Path("/personal/dashboard")

492

@CacheControl(

493

isPrivate = true,

494

maxAge = 5, maxAgeUnit = TimeUnit.MINUTES,

495

mustRevalidate = true

496

)

497

public Dashboard getPersonalDashboard(@Context SecurityContext security) {

498

return dashboardService.getPersonalDashboard(security.getUserPrincipal().getName());

499

}

500

501

// Sensitive data - no caching

502

@GET

503

@Path("/sensitive/data")

504

@CacheControl(noStore = true)

505

public SensitiveResponse getSensitiveData() {

506

return sensitiveService.getData();

507

}

508

}

509

```

510

511

### Error Response Caching

512

513

```java

514

@Path("/errors")

515

public class ErrorCacheResource {

516

517

@GET

518

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

519

public Response getData(@PathParam("id") String id) {

520

try {

521

Data data = dataService.getById(id);

522

523

return Response.ok(data)

524

.header("Cache-Control", "max-age=300") // Cache successful responses

525

.build();

526

527

} catch (NotFoundException e) {

528

return Response.status(404)

529

.entity("Not found")

530

.header("Cache-Control", "max-age=60") // Cache 404s briefly

531

.build();

532

533

} catch (Exception e) {

534

return Response.status(500)

535

.entity("Server error")

536

.header("Cache-Control", "no-cache") // Don't cache server errors

537

.build();

538

}

539

}

540

}

541

```