or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

bean-container.mdbean-invocation.mdbuild-profiles.mdbuild-properties.mdindex.mdinterceptor-integration.mdlogger-injection.mdruntime-lookup.md

interceptor-integration.mddocs/

0

# Interceptor Integration

1

2

Utilities for working with interceptor bindings and context data within interceptor implementations. The InterceptorBindings utility class provides access to Arc-specific interceptor metadata.

3

4

## Capabilities

5

6

### InterceptorBindings Utility Class

7

8

Utility class for accessing interceptor binding metadata from invocation context.

9

10

```java { .api }

11

/**

12

* Utility class for accessing interceptor binding annotations and literals from invocation context.

13

* @see ArcInvocationContext#getInterceptorBindings()

14

*/

15

public final class InterceptorBindings {

16

/**

17

* Retrieves the set of interceptor binding annotations from the invocation context.

18

* @param invocationContext the interceptor invocation context

19

* @return set of interceptor binding annotations

20

*/

21

@SuppressWarnings("unchecked")

22

public static Set<Annotation> getInterceptorBindings(InvocationContext invocationContext);

23

24

/**

25

* This method is just a convenience for getting a hold of AbstractAnnotationLiteral.

26

* See the Javadoc of the class for an explanation of the reasons it might be used.

27

* @param invocationContext the interceptor invocation context

28

* @return set of interceptor binding literals

29

*/

30

@SuppressWarnings("unchecked")

31

public static Set<AbstractAnnotationLiteral> getInterceptorBindingLiterals(InvocationContext invocationContext);

32

}

33

```

34

35

**Usage Examples:**

36

37

```java

38

import jakarta.interceptor.AroundInvoke;

39

import jakarta.interceptor.Interceptor;

40

import jakarta.interceptor.InvocationContext;

41

import io.quarkus.arc.runtime.InterceptorBindings;

42

import java.lang.annotation.Annotation;

43

import java.util.Set;

44

45

// Custom interceptor binding

46

@InterceptorBinding

47

@Retention(RetentionPolicy.RUNTIME)

48

@Target({ElementType.TYPE, ElementType.METHOD})

49

@interface Audited {

50

String value() default "";

51

AuditLevel level() default AuditLevel.INFO;

52

}

53

54

enum AuditLevel {

55

DEBUG, INFO, WARN, ERROR

56

}

57

58

// Another interceptor binding

59

@InterceptorBinding

60

@Retention(RetentionPolicy.RUNTIME)

61

@Target({ElementType.TYPE, ElementType.METHOD})

62

@interface Secured {

63

String[] roles() default {};

64

}

65

66

// Interceptor that uses InterceptorBindings utility

67

@Audited

68

@Interceptor

69

@Priority(Interceptor.Priority.APPLICATION)

70

public class AuditInterceptor {

71

72

@AroundInvoke

73

public Object audit(InvocationContext context) throws Exception {

74

// Get all interceptor bindings

75

Set<Annotation> bindings = InterceptorBindings.getInterceptorBindings(context);

76

77

// Process each binding

78

for (Annotation binding : bindings) {

79

if (binding instanceof Audited) {

80

Audited auditedBinding = (Audited) binding;

81

performAudit(context, auditedBinding.value(), auditedBinding.level());

82

} else if (binding instanceof Secured) {

83

Secured securedBinding = (Secured) binding;

84

checkSecurity(context, securedBinding.roles());

85

}

86

}

87

88

long startTime = System.currentTimeMillis();

89

90

try {

91

Object result = context.proceed();

92

93

// Post-execution audit

94

long duration = System.currentTimeMillis() - startTime;

95

auditMethodExecution(context, duration, true);

96

97

return result;

98

} catch (Exception e) {

99

long duration = System.currentTimeMillis() - startTime;

100

auditMethodExecution(context, duration, false);

101

throw e;

102

}

103

}

104

105

private void performAudit(InvocationContext context, String auditValue, AuditLevel level) {

106

String methodName = context.getMethod().getName();

107

String className = context.getTarget().getClass().getSimpleName();

108

109

String message = String.format("Auditing %s.%s() - %s",

110

className, methodName, auditValue);

111

112

switch (level) {

113

case DEBUG:

114

System.out.println("DEBUG: " + message);

115

break;

116

case INFO:

117

System.out.println("INFO: " + message);

118

break;

119

case WARN:

120

System.out.println("WARN: " + message);

121

break;

122

case ERROR:

123

System.out.println("ERROR: " + message);

124

break;

125

}

126

}

127

128

private void checkSecurity(InvocationContext context, String[] requiredRoles) {

129

// Security check implementation

130

System.out.println("Security check for roles: " + String.join(", ", requiredRoles));

131

// In real implementation, would check current user's roles

132

}

133

134

private void auditMethodExecution(InvocationContext context, long duration, boolean success) {

135

String methodName = context.getMethod().getName();

136

String status = success ? "SUCCESS" : "FAILURE";

137

System.out.println(String.format("Method %s completed in %dms - %s",

138

methodName, duration, status));

139

}

140

}

141

```

142

143

### Advanced Interceptor Binding Analysis

144

145

More sophisticated interceptor implementations using binding metadata.

146

147

```java

148

import jakarta.interceptor.AroundInvoke;

149

import jakarta.interceptor.Interceptor;

150

import jakarta.interceptor.InvocationContext;

151

import io.quarkus.arc.runtime.InterceptorBindings;

152

import io.quarkus.arc.AbstractAnnotationLiteral;

153

import java.lang.annotation.Annotation;

154

import java.util.Set;

155

import java.util.stream.Collectors;

156

157

// Complex interceptor binding with multiple attributes

158

@InterceptorBinding

159

@Retention(RetentionPolicy.RUNTIME)

160

@Target({ElementType.TYPE, ElementType.METHOD})

161

@interface Monitored {

162

String name() default "";

163

MetricType[] metrics() default {MetricType.EXECUTION_TIME};

164

boolean includeParameters() default false;

165

boolean includeResult() default false;

166

String[] tags() default {};

167

}

168

169

enum MetricType {

170

EXECUTION_TIME, INVOCATION_COUNT, ERROR_RATE, PARAMETER_SIZE

171

}

172

173

// Performance monitoring interceptor

174

@Monitored

175

@Interceptor

176

@Priority(Interceptor.Priority.APPLICATION + 10)

177

public class MonitoringInterceptor {

178

179

@AroundInvoke

180

public Object monitor(InvocationContext context) throws Exception {

181

// Get interceptor binding literals for detailed analysis

182

Set<AbstractAnnotationLiteral> bindingLiterals =

183

InterceptorBindings.getInterceptorBindingLiterals(context);

184

185

// Find the Monitored binding

186

Monitored monitoredBinding = findMonitoredBinding(bindingLiterals);

187

188

if (monitoredBinding != null) {

189

return executeWithMonitoring(context, monitoredBinding);

190

} else {

191

// Fallback to default monitoring

192

return executeWithDefaultMonitoring(context);

193

}

194

}

195

196

private Monitored findMonitoredBinding(Set<AbstractAnnotationLiteral> literals) {

197

return literals.stream()

198

.filter(literal -> literal instanceof Monitored)

199

.map(literal -> (Monitored) literal)

200

.findFirst()

201

.orElse(null);

202

}

203

204

private Object executeWithMonitoring(InvocationContext context, Monitored binding) throws Exception {

205

String methodName = getMethodName(context);

206

String monitorName = binding.name().isEmpty() ? methodName : binding.name();

207

208

// Pre-execution monitoring

209

MetricCollector collector = new MetricCollector(monitorName);

210

211

if (binding.includeParameters()) {

212

collector.recordParameters(context.getParameters());

213

}

214

215

for (MetricType metricType : binding.metrics()) {

216

collector.startMetric(metricType);

217

}

218

219

long startTime = System.currentTimeMillis();

220

221

try {

222

Object result = context.proceed();

223

224

// Post-execution success monitoring

225

long duration = System.currentTimeMillis() - startTime;

226

collector.recordSuccess(duration);

227

228

if (binding.includeResult()) {

229

collector.recordResult(result);

230

}

231

232

// Record custom tags

233

for (String tag : binding.tags()) {

234

collector.addTag(tag);

235

}

236

237

return result;

238

} catch (Exception e) {

239

// Post-execution error monitoring

240

long duration = System.currentTimeMillis() - startTime;

241

collector.recordError(duration, e);

242

throw e;

243

} finally {

244

collector.flush();

245

}

246

}

247

248

private Object executeWithDefaultMonitoring(InvocationContext context) throws Exception {

249

String methodName = getMethodName(context);

250

long startTime = System.currentTimeMillis();

251

252

try {

253

Object result = context.proceed();

254

long duration = System.currentTimeMillis() - startTime;

255

System.out.println(String.format("Method %s executed successfully in %dms",

256

methodName, duration));

257

return result;

258

} catch (Exception e) {

259

long duration = System.currentTimeMillis() - startTime;

260

System.out.println(String.format("Method %s failed after %dms: %s",

261

methodName, duration, e.getMessage()));

262

throw e;

263

}

264

}

265

266

private String getMethodName(InvocationContext context) {

267

return context.getTarget().getClass().getSimpleName() + "." +

268

context.getMethod().getName();

269

}

270

}

271

272

// Metric collection utility

273

class MetricCollector {

274

private final String name;

275

private final Map<MetricType, Long> startTimes = new HashMap<>();

276

private final List<String> tags = new ArrayList<>();

277

278

public MetricCollector(String name) {

279

this.name = name;

280

}

281

282

public void startMetric(MetricType type) {

283

startTimes.put(type, System.currentTimeMillis());

284

}

285

286

public void recordParameters(Object[] parameters) {

287

System.out.println("Parameters for " + name + ": " +

288

Arrays.toString(parameters));

289

}

290

291

public void recordResult(Object result) {

292

System.out.println("Result for " + name + ": " + result);

293

}

294

295

public void recordSuccess(long duration) {

296

System.out.println("Success - " + name + " took " + duration + "ms");

297

}

298

299

public void recordError(long duration, Exception error) {

300

System.out.println("Error - " + name + " failed after " + duration + "ms: " +

301

error.getMessage());

302

}

303

304

public void addTag(String tag) {

305

tags.add(tag);

306

}

307

308

public void flush() {

309

if (!tags.isEmpty()) {

310

System.out.println("Tags for " + name + ": " + String.join(", ", tags));

311

}

312

}

313

}

314

```

315

316

### Multi-Binding Interceptor

317

318

Interceptor that handles multiple different binding types using the InterceptorBindings utility.

319

320

```java

321

import jakarta.interceptor.AroundInvoke;

322

import jakarta.interceptor.Interceptor;

323

import jakarta.interceptor.InvocationContext;

324

import io.quarkus.arc.runtime.InterceptorBindings;

325

import java.lang.annotation.Annotation;

326

import java.util.Set;

327

328

// Multiple interceptor bindings

329

@InterceptorBinding

330

@Retention(RetentionPolicy.RUNTIME)

331

@Target({ElementType.TYPE, ElementType.METHOD})

332

@interface Cached {

333

int ttlSeconds() default 300;

334

String keyPrefix() default "";

335

}

336

337

@InterceptorBinding

338

@Retention(RetentionPolicy.RUNTIME)

339

@Target({ElementType.TYPE, ElementType.METHOD})

340

@interface RateLimited {

341

int requestsPerMinute() default 60;

342

String identifier() default "";

343

}

344

345

@InterceptorBinding

346

@Retention(RetentionPolicy.RUNTIME)

347

@Target({ElementType.TYPE, ElementType.METHOD})

348

@interface Validated {

349

Class<?>[] groups() default {};

350

boolean failFast() default true;

351

}

352

353

// Multi-purpose interceptor

354

@Cached

355

@RateLimited

356

@Validated

357

@Interceptor

358

@Priority(Interceptor.Priority.APPLICATION + 20)

359

public class MultiPurposeInterceptor {

360

361

@AroundInvoke

362

public Object intercept(InvocationContext context) throws Exception {

363

Set<Annotation> bindings = InterceptorBindings.getInterceptorBindings(context);

364

365

// Process each type of binding

366

InterceptorChain chain = new InterceptorChain(context);

367

368

for (Annotation binding : bindings) {

369

if (binding instanceof Cached) {

370

chain.addHandler(new CacheHandler((Cached) binding));

371

} else if (binding instanceof RateLimited) {

372

chain.addHandler(new RateLimitHandler((RateLimited) binding));

373

} else if (binding instanceof Validated) {

374

chain.addHandler(new ValidationHandler((Validated) binding));

375

}

376

}

377

378

return chain.execute();

379

}

380

}

381

382

// Interceptor chain pattern

383

class InterceptorChain {

384

private final InvocationContext context;

385

private final List<InterceptorHandler> handlers = new ArrayList<>();

386

387

public InterceptorChain(InvocationContext context) {

388

this.context = context;

389

}

390

391

public void addHandler(InterceptorHandler handler) {

392

handlers.add(handler);

393

}

394

395

public Object execute() throws Exception {

396

return executeHandlers(0);

397

}

398

399

private Object executeHandlers(int index) throws Exception {

400

if (index >= handlers.size()) {

401

return context.proceed();

402

}

403

404

InterceptorHandler handler = handlers.get(index);

405

return handler.handle(context, () -> executeHandlers(index + 1));

406

}

407

}

408

409

// Handler interface

410

interface InterceptorHandler {

411

Object handle(InvocationContext context, HandlerChain next) throws Exception;

412

}

413

414

@FunctionalInterface

415

interface HandlerChain {

416

Object proceed() throws Exception;

417

}

418

419

// Specific handlers

420

class CacheHandler implements InterceptorHandler {

421

private final Cached cacheConfig;

422

423

public CacheHandler(Cached cacheConfig) {

424

this.cacheConfig = cacheConfig;

425

}

426

427

@Override

428

public Object handle(InvocationContext context, HandlerChain next) throws Exception {

429

String cacheKey = generateCacheKey(context);

430

431

// Check cache

432

Object cachedResult = getFromCache(cacheKey);

433

if (cachedResult != null) {

434

System.out.println("Cache hit for key: " + cacheKey);

435

return cachedResult;

436

}

437

438

// Execute and cache result

439

Object result = next.proceed();

440

putInCache(cacheKey, result, cacheConfig.ttlSeconds());

441

System.out.println("Cached result for key: " + cacheKey);

442

443

return result;

444

}

445

446

private String generateCacheKey(InvocationContext context) {

447

String methodName = context.getMethod().getName();

448

String params = Arrays.toString(context.getParameters());

449

String prefix = cacheConfig.keyPrefix().isEmpty() ? "" : cacheConfig.keyPrefix() + ":";

450

return prefix + methodName + ":" + params.hashCode();

451

}

452

453

private Object getFromCache(String key) {

454

// Simplified cache implementation

455

return null; // Cache miss

456

}

457

458

private void putInCache(String key, Object value, int ttlSeconds) {

459

// Simplified cache implementation

460

System.out.println("Storing in cache: " + key + " for " + ttlSeconds + " seconds");

461

}

462

}

463

464

class RateLimitHandler implements InterceptorHandler {

465

private final RateLimited rateLimitConfig;

466

467

public RateLimitHandler(RateLimited rateLimitConfig) {

468

this.rateLimitConfig = rateLimitConfig;

469

}

470

471

@Override

472

public Object handle(InvocationContext context, HandlerChain next) throws Exception {

473

String identifier = rateLimitConfig.identifier().isEmpty() ?

474

"default" : rateLimitConfig.identifier();

475

476

if (!checkRateLimit(identifier, rateLimitConfig.requestsPerMinute())) {

477

throw new RateLimitExceededException(

478

"Rate limit exceeded: " + rateLimitConfig.requestsPerMinute() + " requests per minute");

479

}

480

481

return next.proceed();

482

}

483

484

private boolean checkRateLimit(String identifier, int requestsPerMinute) {

485

// Simplified rate limiting implementation

486

System.out.println("Checking rate limit for: " + identifier +

487

" (" + requestsPerMinute + " requests/minute)");

488

return true; // Allow for demo

489

}

490

}

491

492

class ValidationHandler implements InterceptorHandler {

493

private final Validated validationConfig;

494

495

public ValidationHandler(Validated validationConfig) {

496

this.validationConfig = validationConfig;

497

}

498

499

@Override

500

public Object handle(InvocationContext context, HandlerChain next) throws Exception {

501

Object[] parameters = context.getParameters();

502

503

for (Object param : parameters) {

504

if (param != null) {

505

validateParameter(param, validationConfig.groups(), validationConfig.failFast());

506

}

507

}

508

509

return next.proceed();

510

}

511

512

private void validateParameter(Object param, Class<?>[] groups, boolean failFast) {

513

// Simplified validation implementation

514

System.out.println("Validating parameter: " + param.getClass().getSimpleName() +

515

" with groups: " + Arrays.toString(groups) + ", failFast: " + failFast);

516

}

517

}

518

519

// Custom exception

520

class RateLimitExceededException extends RuntimeException {

521

public RateLimitExceededException(String message) {

522

super(message);

523

}

524

}

525

```

526

527

### Usage Examples

528

529

Examples of using the interceptor bindings in service classes.

530

531

```java

532

import jakarta.enterprise.context.ApplicationScoped;

533

534

@ApplicationScoped

535

public class BusinessService {

536

537

// Method with multiple interceptor bindings

538

@Audited(value = "user-creation", level = AuditLevel.INFO)

539

@Secured(roles = {"admin", "user-manager"})

540

@Monitored(name = "create-user",

541

metrics = {MetricType.EXECUTION_TIME, MetricType.INVOCATION_COUNT},

542

includeParameters = true,

543

tags = {"business", "user-management"})

544

public User createUser(String username, String email) {

545

// Business logic

546

return new User(username, email);

547

}

548

549

// Cached method

550

@Cached(ttlSeconds = 600, keyPrefix = "user-lookup")

551

@Monitored(metrics = {MetricType.EXECUTION_TIME})

552

public User findUser(String username) {

553

// Expensive lookup operation

554

return performUserLookup(username);

555

}

556

557

// Rate limited method

558

@RateLimited(requestsPerMinute = 10, identifier = "email-sender")

559

@Audited(value = "email-sent", level = AuditLevel.INFO)

560

public void sendEmail(String to, String subject, String body) {

561

// Email sending logic

562

performEmailSend(to, subject, body);

563

}

564

565

// Validated method

566

@Validated(groups = {ValidationGroup.Create.class}, failFast = true)

567

@Audited(value = "order-processing", level = AuditLevel.WARN)

568

public Order processOrder(OrderRequest request) {

569

// Order processing logic

570

return new Order(request);

571

}

572

573

// Multiple caching and monitoring

574

@Cached(ttlSeconds = 300, keyPrefix = "reports")

575

@Monitored(name = "generate-report",

576

includeParameters = true,

577

includeResult = false,

578

tags = {"reports", "business-intelligence"})

579

public ReportData generateReport(String reportType, Date fromDate, Date toDate) {

580

// Report generation logic

581

return new ReportData(reportType, fromDate, toDate);

582

}

583

584

// Helper methods (not intercepted)

585

private User performUserLookup(String username) {

586

// Database lookup

587

return new User(username, username + "@example.com");

588

}

589

590

private void performEmailSend(String to, String subject, String body) {

591

System.out.println("Sending email to: " + to + ", subject: " + subject);

592

}

593

}

594

595

// Supporting classes

596

class User {

597

private final String username;

598

private final String email;

599

600

public User(String username, String email) {

601

this.username = username;

602

this.email = email;

603

}

604

605

// getters...

606

}

607

608

class Order {

609

private final OrderRequest request;

610

611

public Order(OrderRequest request) {

612

this.request = request;

613

}

614

}

615

616

class OrderRequest {

617

// Order request fields

618

}

619

620

class ReportData {

621

private final String type;

622

private final Date fromDate;

623

private final Date toDate;

624

625

public ReportData(String type, Date fromDate, Date toDate) {

626

this.type = type;

627

this.fromDate = fromDate;

628

this.toDate = toDate;

629

}

630

}

631

632

interface ValidationGroup {

633

interface Create {}

634

interface Update {}

635

}

636

```

637

638

## Best Practices

639

640

### Defensive Programming

641

642

```java

643

@Interceptor

644

public class SafeInterceptor {

645

646

@AroundInvoke

647

public Object safeIntercept(InvocationContext context) throws Exception {

648

try {

649

Set<Annotation> bindings = InterceptorBindings.getInterceptorBindings(context);

650

651

if (bindings == null || bindings.isEmpty()) {

652

// No bindings found, proceed without special handling

653

return context.proceed();

654

}

655

656

// Process bindings safely

657

return processBindings(context, bindings);

658

659

} catch (Exception e) {

660

// Log error but don't prevent method execution

661

System.err.println("Interceptor error: " + e.getMessage());

662

return context.proceed();

663

}

664

}

665

666

private Object processBindings(InvocationContext context, Set<Annotation> bindings) throws Exception {

667

// Safe binding processing

668

for (Annotation binding : bindings) {

669

if (binding != null) {

670

processBinding(context, binding);

671

}

672

}

673

return context.proceed();

674

}

675

676

private void processBinding(InvocationContext context, Annotation binding) {

677

// Individual binding processing with error handling

678

try {

679

// Process specific binding

680

} catch (Exception e) {

681

System.err.println("Error processing binding " + binding.getClass().getSimpleName() +

682

": " + e.getMessage());

683

}

684

}

685

}

686

```

687

688

### Performance Considerations

689

690

```java

691

@Interceptor

692

public class OptimizedInterceptor {

693

694

// Cache binding analysis results

695

private final Map<Method, Set<Annotation>> bindingCache = new ConcurrentHashMap<>();

696

697

@AroundInvoke

698

public Object optimizedIntercept(InvocationContext context) throws Exception {

699

Method method = context.getMethod();

700

701

// Use cached bindings if available

702

Set<Annotation> bindings = bindingCache.computeIfAbsent(method,

703

m -> InterceptorBindings.getInterceptorBindings(context));

704

705

if (bindings.isEmpty()) {

706

return context.proceed();

707

}

708

709

// Process with cached bindings

710

return processOptimized(context, bindings);

711

}

712

713

private Object processOptimized(InvocationContext context, Set<Annotation> bindings) throws Exception {

714

// Optimized processing logic

715

return context.proceed();

716

}

717

}

718

```