or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

async-processing.mdhttp-processing.mdindex.mdlisteners-events.mdsecurity-filtering.mdservlet-lifecycle.mdsession-cookies.md

listeners-events.mddocs/

0

# Listeners and Events

1

2

The Java Servlet API provides a comprehensive event-driven architecture through various listener interfaces. This enables applications to respond to lifecycle events, attribute changes, and other significant events in the servlet container.

3

4

## ServletContext Listeners

5

6

### ServletContextListener Interface

7

8

```java { .api }

9

/**

10

* Listener interface for receiving notifications about ServletContext

11

* lifecycle changes (application startup and shutdown).

12

*/

13

public interface ServletContextListener extends EventListener {

14

15

/**

16

* Called when the ServletContext is initialized (application startup).

17

* This method is invoked before any servlets, filters, or listeners

18

* are initialized.

19

*/

20

void contextInitialized(ServletContextEvent sce);

21

22

/**

23

* Called when the ServletContext is about to be destroyed (application shutdown).

24

* This method is invoked after all servlets and filters have been destroyed.

25

*/

26

void contextDestroyed(ServletContextEvent sce);

27

}

28

```

29

30

### ServletContextAttributeListener Interface

31

32

```java { .api }

33

/**

34

* Listener interface for receiving notifications about ServletContext

35

* attribute changes.

36

*/

37

public interface ServletContextAttributeListener extends EventListener {

38

39

/**

40

* Called when an attribute is added to the ServletContext.

41

*/

42

void attributeAdded(ServletContextAttributeEvent event);

43

44

/**

45

* Called when an attribute is removed from the ServletContext.

46

*/

47

void attributeRemoved(ServletContextAttributeEvent event);

48

49

/**

50

* Called when an attribute in the ServletContext is replaced.

51

*/

52

void attributeReplaced(ServletContextAttributeEvent event);

53

}

54

```

55

56

## ServletContext Event Classes

57

58

```java { .api }

59

/**

60

* Event class for ServletContext lifecycle events.

61

*/

62

public class ServletContextEvent extends java.util.EventObject {

63

64

/**

65

* Create a ServletContextEvent from the given ServletContext.

66

*/

67

public ServletContextEvent(ServletContext source) {

68

super(source);

69

}

70

71

/**

72

* Get the ServletContext that changed.

73

*/

74

public ServletContext getServletContext() {

75

return (ServletContext) super.getSource();

76

}

77

}

78

79

/**

80

* Event class for ServletContext attribute changes.

81

*/

82

public class ServletContextAttributeEvent extends ServletContextEvent {

83

84

private String name;

85

private Object value;

86

87

/**

88

* Create a ServletContextAttributeEvent.

89

*/

90

public ServletContextAttributeEvent(ServletContext source, String name, Object value) {

91

super(source);

92

this.name = name;

93

this.value = value;

94

}

95

96

/**

97

* Get the name of the attribute that changed.

98

*/

99

public String getName() {

100

return name;

101

}

102

103

/**

104

* Get the value of the attribute.

105

* For attributeAdded: the value that was added.

106

* For attributeRemoved: the value that was removed.

107

* For attributeReplaced: the old value that was replaced.

108

*/

109

public Object getValue() {

110

return value;

111

}

112

}

113

```

114

115

## ServletRequest Listeners

116

117

### ServletRequestListener Interface

118

119

```java { .api }

120

/**

121

* Listener interface for receiving notifications about ServletRequest

122

* lifecycle events (request initialization and destruction).

123

*/

124

public interface ServletRequestListener extends EventListener {

125

126

/**

127

* Called when a ServletRequest is about to go out of scope.

128

*/

129

void requestDestroyed(ServletRequestEvent sre);

130

131

/**

132

* Called when a ServletRequest is about to come into scope.

133

*/

134

void requestInitialized(ServletRequestEvent sre);

135

}

136

```

137

138

### ServletRequestAttributeListener Interface

139

140

```java { .api }

141

/**

142

* Listener interface for receiving notifications about ServletRequest

143

* attribute changes.

144

*/

145

public interface ServletRequestAttributeListener extends EventListener {

146

147

/**

148

* Called when an attribute is added to the ServletRequest.

149

*/

150

void attributeAdded(ServletRequestAttributeEvent srae);

151

152

/**

153

* Called when an attribute is removed from the ServletRequest.

154

*/

155

void attributeRemoved(ServletRequestAttributeEvent srae);

156

157

/**

158

* Called when an attribute in the ServletRequest is replaced.

159

*/

160

void attributeReplaced(ServletRequestAttributeEvent srae);

161

}

162

```

163

164

## ServletRequest Event Classes

165

166

```java { .api }

167

/**

168

* Event class for ServletRequest lifecycle events.

169

*/

170

public class ServletRequestEvent extends java.util.EventObject {

171

172

private ServletContext servletContext;

173

174

/**

175

* Create a ServletRequestEvent.

176

*/

177

public ServletRequestEvent(ServletContext sc, ServletRequest request) {

178

super(request);

179

this.servletContext = sc;

180

}

181

182

/**

183

* Get the ServletRequest that is changing.

184

*/

185

public ServletRequest getServletRequest() {

186

return (ServletRequest) super.getSource();

187

}

188

189

/**

190

* Get the ServletContext of the request.

191

*/

192

public ServletContext getServletContext() {

193

return servletContext;

194

}

195

}

196

197

/**

198

* Event class for ServletRequest attribute changes.

199

*/

200

public class ServletRequestAttributeEvent extends ServletRequestEvent {

201

202

private String name;

203

private Object value;

204

205

/**

206

* Create a ServletRequestAttributeEvent.

207

*/

208

public ServletRequestAttributeEvent(ServletContext sc, ServletRequest request,

209

String name, Object value) {

210

super(sc, request);

211

this.name = name;

212

this.value = value;

213

}

214

215

/**

216

* Get the name of the attribute that changed.

217

*/

218

public String getName() {

219

return name;

220

}

221

222

/**

223

* Get the value of the attribute.

224

*/

225

public Object getValue() {

226

return value;

227

}

228

}

229

```

230

231

## HTTP Session Listeners

232

233

### HttpSessionListener Interface

234

235

```java { .api }

236

/**

237

* Listener interface for receiving notifications about HttpSession

238

* lifecycle changes (session creation and destruction).

239

*/

240

public interface HttpSessionListener extends EventListener {

241

242

/**

243

* Called when an HttpSession is created.

244

*/

245

void sessionCreated(HttpSessionEvent se);

246

247

/**

248

* Called when an HttpSession is destroyed (invalidated or timed out).

249

*/

250

void sessionDestroyed(HttpSessionEvent se);

251

}

252

```

253

254

### HttpSessionAttributeListener Interface

255

256

```java { .api }

257

/**

258

* Listener interface for receiving notifications about HttpSession

259

* attribute changes.

260

*/

261

public interface HttpSessionAttributeListener extends EventListener {

262

263

/**

264

* Called when an attribute is added to an HttpSession.

265

*/

266

void attributeAdded(HttpSessionBindingEvent event);

267

268

/**

269

* Called when an attribute is removed from an HttpSession.

270

*/

271

void attributeRemoved(HttpSessionBindingEvent event);

272

273

/**

274

* Called when an attribute in an HttpSession is replaced.

275

*/

276

void attributeReplaced(HttpSessionBindingEvent event);

277

}

278

```

279

280

### HttpSessionActivationListener Interface

281

282

```java { .api }

283

/**

284

* Listener interface for objects that need to be notified when

285

* they are bound/unbound from a session that is being passivated/activated.

286

*/

287

public interface HttpSessionActivationListener extends EventListener {

288

289

/**

290

* Called when the session containing this object is about to be passivated.

291

* Objects can use this notification to release resources or prepare for serialization.

292

*/

293

void sessionWillPassivate(HttpSessionEvent se);

294

295

/**

296

* Called when the session containing this object has been activated.

297

* Objects can use this notification to reacquire resources.

298

*/

299

void sessionDidActivate(HttpSessionEvent se);

300

}

301

```

302

303

### HttpSessionBindingListener Interface

304

305

```java { .api }

306

/**

307

* Listener interface for objects that want to be notified when

308

* they are bound to or unbound from an HttpSession.

309

*/

310

public interface HttpSessionBindingListener extends EventListener {

311

312

/**

313

* Called when the object is bound to a session.

314

*/

315

void valueBound(HttpSessionBindingEvent event);

316

317

/**

318

* Called when the object is unbound from a session.

319

*/

320

void valueUnbound(HttpSessionBindingEvent event);

321

}

322

```

323

324

### HttpSessionIdListener Interface

325

326

```java { .api }

327

/**

328

* Listener interface for receiving notifications about HttpSession ID changes.

329

*/

330

public interface HttpSessionIdListener extends EventListener {

331

332

/**

333

* Called when an HttpSession ID is changed.

334

*/

335

void sessionIdChanged(HttpSessionEvent event, String oldSessionId);

336

}

337

```

338

339

## HTTP Session Event Classes

340

341

```java { .api }

342

/**

343

* Event class for HttpSession lifecycle and ID change events.

344

*/

345

public class HttpSessionEvent extends java.util.EventObject {

346

347

/**

348

* Create an HttpSessionEvent from the given HttpSession.

349

*/

350

public HttpSessionEvent(HttpSession source) {

351

super(source);

352

}

353

354

/**

355

* Get the HttpSession that changed.

356

*/

357

public HttpSession getSession() {

358

return (HttpSession) super.getSource();

359

}

360

}

361

362

/**

363

* Event class for HttpSession attribute binding operations.

364

*/

365

public class HttpSessionBindingEvent extends HttpSessionEvent {

366

367

private String name;

368

private Object value;

369

370

/**

371

* Create an HttpSessionBindingEvent.

372

*/

373

public HttpSessionBindingEvent(HttpSession session, String name) {

374

super(session);

375

this.name = name;

376

}

377

378

/**

379

* Create an HttpSessionBindingEvent with a value.

380

*/

381

public HttpSessionBindingEvent(HttpSession session, String name, Object value) {

382

super(session);

383

this.name = name;

384

this.value = value;

385

}

386

387

/**

388

* Get the name of the attribute that was bound/unbound.

389

*/

390

public String getName() {

391

return name;

392

}

393

394

/**

395

* Get the value of the attribute.

396

*/

397

public Object getValue() {

398

return value;

399

}

400

}

401

```

402

403

## Listener Implementation Examples

404

405

### Application Lifecycle Manager

406

407

```java { .api }

408

/**

409

* Comprehensive application lifecycle listener that manages startup and shutdown

410

*/

411

@WebListener

412

public class ApplicationLifecycleManager implements ServletContextListener,

413

ServletContextAttributeListener {

414

415

private static final Logger logger = LoggerFactory.getLogger(ApplicationLifecycleManager.class);

416

417

@Override

418

public void contextInitialized(ServletContextEvent sce) {

419

ServletContext context = sce.getServletContext();

420

421

logger.info("Application startup initiated");

422

423

try {

424

// Initialize application components

425

initializeDatabase(context);

426

initializeScheduler(context);

427

initializeCaching(context);

428

initializeExternalServices(context);

429

430

// Set application metadata

431

context.setAttribute("app.startTime", System.currentTimeMillis());

432

context.setAttribute("app.version", getClass().getPackage().getImplementationVersion());

433

context.setAttribute("app.status", "RUNNING");

434

435

// Initialize application configuration

436

Properties config = loadApplicationConfig();

437

context.setAttribute("app.config", config);

438

439

// Start background tasks

440

startBackgroundTasks(context);

441

442

logger.info("Application startup completed successfully");

443

444

} catch (Exception e) {

445

logger.error("Application startup failed", e);

446

context.setAttribute("app.status", "FAILED");

447

throw new RuntimeException("Application initialization failed", e);

448

}

449

}

450

451

@Override

452

public void contextDestroyed(ServletContextEvent sce) {

453

ServletContext context = sce.getServletContext();

454

455

logger.info("Application shutdown initiated");

456

457

try {

458

// Set shutdown status

459

context.setAttribute("app.status", "SHUTTING_DOWN");

460

461

// Stop background tasks

462

stopBackgroundTasks(context);

463

464

// Shutdown application components in reverse order

465

shutdownExternalServices(context);

466

shutdownCaching(context);

467

shutdownScheduler(context);

468

shutdownDatabase(context);

469

470

// Clear application attributes

471

clearApplicationAttributes(context);

472

473

logger.info("Application shutdown completed successfully");

474

475

} catch (Exception e) {

476

logger.error("Error during application shutdown", e);

477

}

478

}

479

480

@Override

481

public void attributeAdded(ServletContextAttributeEvent event) {

482

String name = event.getName();

483

Object value = event.getValue();

484

485

logger.debug("ServletContext attribute added: {} = {}", name, value);

486

487

// Monitor critical attributes

488

if ("app.status".equals(name)) {

489

logger.info("Application status changed to: {}", value);

490

}

491

}

492

493

@Override

494

public void attributeRemoved(ServletContextAttributeEvent event) {

495

String name = event.getName();

496

Object value = event.getValue();

497

498

logger.debug("ServletContext attribute removed: {} (was: {})", name, value);

499

}

500

501

@Override

502

public void attributeReplaced(ServletContextAttributeEvent event) {

503

String name = event.getName();

504

Object newValue = event.getServletContext().getAttribute(name);

505

Object oldValue = event.getValue();

506

507

logger.debug("ServletContext attribute replaced: {} = {} (was: {})",

508

name, newValue, oldValue);

509

}

510

511

private void initializeDatabase(ServletContext context) {

512

// Initialize database connections and connection pool

513

logger.info("Initializing database connections");

514

515

DataSource dataSource = createDataSource();

516

context.setAttribute("app.dataSource", dataSource);

517

518

// Test database connection

519

try (Connection conn = dataSource.getConnection()) {

520

logger.info("Database connection established successfully");

521

} catch (SQLException e) {

522

throw new RuntimeException("Failed to establish database connection", e);

523

}

524

}

525

526

private void initializeScheduler(ServletContext context) {

527

logger.info("Initializing task scheduler");

528

529

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(5);

530

context.setAttribute("app.scheduler", scheduler);

531

532

// Schedule periodic maintenance tasks

533

scheduler.scheduleAtFixedRate(() -> {

534

logger.debug("Running periodic maintenance");

535

performMaintenance();

536

}, 1, 1, TimeUnit.HOURS);

537

}

538

539

private void initializeCaching(ServletContext context) {

540

logger.info("Initializing cache system");

541

542

// Initialize cache manager (e.g., EhCache, Redis, etc.)

543

CacheManager cacheManager = createCacheManager();

544

context.setAttribute("app.cacheManager", cacheManager);

545

}

546

547

private void initializeExternalServices(ServletContext context) {

548

logger.info("Initializing external service connections");

549

550

// Initialize HTTP clients, web service clients, etc.

551

HttpClient httpClient = HttpClient.newBuilder()

552

.connectTimeout(Duration.ofSeconds(30))

553

.build();

554

context.setAttribute("app.httpClient", httpClient);

555

}

556

557

private void startBackgroundTasks(ServletContext context) {

558

logger.info("Starting background tasks");

559

560

ScheduledExecutorService scheduler =

561

(ScheduledExecutorService) context.getAttribute("app.scheduler");

562

563

// Start application-specific background tasks

564

scheduler.scheduleAtFixedRate(new SessionCleanupTask(), 0, 30, TimeUnit.MINUTES);

565

scheduler.scheduleAtFixedRate(new LogRotationTask(), 1, 24, TimeUnit.HOURS);

566

}

567

568

private Properties loadApplicationConfig() {

569

Properties config = new Properties();

570

571

try (InputStream input = getClass().getResourceAsStream("/application.properties")) {

572

if (input != null) {

573

config.load(input);

574

}

575

} catch (IOException e) {

576

logger.warn("Could not load application.properties", e);

577

}

578

579

return config;

580

}

581

582

// Shutdown methods

583

private void stopBackgroundTasks(ServletContext context) {

584

ScheduledExecutorService scheduler =

585

(ScheduledExecutorService) context.getAttribute("app.scheduler");

586

587

if (scheduler != null && !scheduler.isShutdown()) {

588

scheduler.shutdown();

589

try {

590

if (!scheduler.awaitTermination(30, TimeUnit.SECONDS)) {

591

scheduler.shutdownNow();

592

}

593

} catch (InterruptedException e) {

594

scheduler.shutdownNow();

595

}

596

}

597

}

598

599

private void shutdownDatabase(ServletContext context) {

600

DataSource dataSource = (DataSource) context.getAttribute("app.dataSource");

601

if (dataSource instanceof Closeable) {

602

try {

603

((Closeable) dataSource).close();

604

logger.info("Database connections closed");

605

} catch (IOException e) {

606

logger.error("Error closing database connections", e);

607

}

608

}

609

}

610

611

// Helper methods

612

private DataSource createDataSource() {

613

// Create and configure DataSource

614

return null; // Implementation specific

615

}

616

617

private CacheManager createCacheManager() {

618

// Create and configure cache manager

619

return null; // Implementation specific

620

}

621

622

private void performMaintenance() {

623

// Perform periodic maintenance tasks

624

}

625

626

private void shutdownExternalServices(ServletContext context) {

627

// Close external service connections

628

}

629

630

private void shutdownCaching(ServletContext context) {

631

// Shutdown cache manager

632

}

633

634

private void shutdownScheduler(ServletContext context) {

635

// Already handled in stopBackgroundTasks

636

}

637

638

private void clearApplicationAttributes(ServletContext context) {

639

// Clear non-essential attributes

640

List<String> attributesToClear = Arrays.asList(

641

"app.dataSource", "app.scheduler", "app.cacheManager", "app.httpClient"

642

);

643

644

for (String attribute : attributesToClear) {

645

context.removeAttribute(attribute);

646

}

647

}

648

649

// Background task implementations

650

private static class SessionCleanupTask implements Runnable {

651

@Override

652

public void run() {

653

// Cleanup expired sessions, temporary files, etc.

654

}

655

}

656

657

private static class LogRotationTask implements Runnable {

658

@Override

659

public void run() {

660

// Rotate application logs

661

}

662

}

663

}

664

```

665

666

### Session Activity Monitor

667

668

```java { .api }

669

/**

670

* Listener that monitors HTTP session activity and user behavior

671

*/

672

@WebListener

673

public class SessionActivityMonitor implements HttpSessionListener,

674

HttpSessionAttributeListener,

675

HttpSessionActivationListener,

676

HttpSessionIdListener {

677

678

private static final Logger logger = LoggerFactory.getLogger(SessionActivityMonitor.class);

679

private static final AtomicLong activeSessions = new AtomicLong(0);

680

private static final Map<String, SessionInfo> sessionRegistry = new ConcurrentHashMap<>();

681

682

@Override

683

public void sessionCreated(HttpSessionEvent se) {

684

HttpSession session = se.getSession();

685

long sessionCount = activeSessions.incrementAndGet();

686

687

SessionInfo sessionInfo = new SessionInfo(

688

session.getId(),

689

session.getCreationTime(),

690

session.getMaxInactiveInterval()

691

);

692

sessionRegistry.put(session.getId(), sessionInfo);

693

694

logger.info("Session created: {} (Total active: {})", session.getId(), sessionCount);

695

696

// Store session metrics in servlet context

697

ServletContext context = session.getServletContext();

698

context.setAttribute("sessions.active.count", sessionCount);

699

context.setAttribute("sessions.total.created",

700

((Long) context.getAttribute("sessions.total.created")) + 1);

701

}

702

703

@Override

704

public void sessionDestroyed(HttpSessionEvent se) {

705

HttpSession session = se.getSession();

706

long sessionCount = activeSessions.decrementAndGet();

707

708

SessionInfo sessionInfo = sessionRegistry.remove(session.getId());

709

if (sessionInfo != null) {

710

long duration = System.currentTimeMillis() - sessionInfo.getCreationTime();

711

logger.info("Session destroyed: {} (Duration: {}ms, Total active: {})",

712

session.getId(), duration, sessionCount);

713

714

// Log session statistics

715

logSessionStatistics(sessionInfo, duration);

716

}

717

718

// Update servlet context metrics

719

ServletContext context = session.getServletContext();

720

context.setAttribute("sessions.active.count", sessionCount);

721

}

722

723

@Override

724

public void attributeAdded(HttpSessionBindingEvent event) {

725

String sessionId = event.getSession().getId();

726

String attributeName = event.getName();

727

Object attributeValue = event.getValue();

728

729

logger.debug("Session attribute added: {} -> {} = {}",

730

sessionId, attributeName, attributeValue);

731

732

// Track user login

733

if ("authenticated".equals(attributeName) && Boolean.TRUE.equals(attributeValue)) {

734

String username = (String) event.getSession().getAttribute("username");

735

logUserLogin(sessionId, username);

736

}

737

738

// Update session info

739

SessionInfo sessionInfo = sessionRegistry.get(sessionId);

740

if (sessionInfo != null) {

741

sessionInfo.addAttribute(attributeName, attributeValue);

742

}

743

}

744

745

@Override

746

public void attributeRemoved(HttpSessionBindingEvent event) {

747

String sessionId = event.getSession().getId();

748

String attributeName = event.getName();

749

Object attributeValue = event.getValue();

750

751

logger.debug("Session attribute removed: {} -> {} (was: {})",

752

sessionId, attributeName, attributeValue);

753

754

// Track user logout

755

if ("authenticated".equals(attributeName)) {

756

String username = (String) attributeValue;

757

logUserLogout(sessionId, username);

758

}

759

760

// Update session info

761

SessionInfo sessionInfo = sessionRegistry.get(sessionId);

762

if (sessionInfo != null) {

763

sessionInfo.removeAttribute(attributeName);

764

}

765

}

766

767

@Override

768

public void attributeReplaced(HttpSessionBindingEvent event) {

769

String sessionId = event.getSession().getId();

770

String attributeName = event.getName();

771

Object oldValue = event.getValue();

772

Object newValue = event.getSession().getAttribute(attributeName);

773

774

logger.debug("Session attribute replaced: {} -> {} = {} (was: {})",

775

sessionId, attributeName, newValue, oldValue);

776

777

// Update session info

778

SessionInfo sessionInfo = sessionRegistry.get(sessionId);

779

if (sessionInfo != null) {

780

sessionInfo.updateAttribute(attributeName, newValue);

781

}

782

}

783

784

@Override

785

public void sessionWillPassivate(HttpSessionEvent se) {

786

String sessionId = se.getSession().getId();

787

logger.debug("Session will passivate: {}", sessionId);

788

789

SessionInfo sessionInfo = sessionRegistry.get(sessionId);

790

if (sessionInfo != null) {

791

sessionInfo.setPassivated(true);

792

}

793

}

794

795

@Override

796

public void sessionDidActivate(HttpSessionEvent se) {

797

String sessionId = se.getSession().getId();

798

logger.debug("Session did activate: {}", sessionId);

799

800

SessionInfo sessionInfo = sessionRegistry.get(sessionId);

801

if (sessionInfo != null) {

802

sessionInfo.setPassivated(false);

803

}

804

}

805

806

@Override

807

public void sessionIdChanged(HttpSessionEvent event, String oldSessionId) {

808

HttpSession session = event.getSession();

809

String newSessionId = session.getId();

810

811

logger.info("Session ID changed: {} -> {}", oldSessionId, newSessionId);

812

813

// Update session registry

814

SessionInfo sessionInfo = sessionRegistry.remove(oldSessionId);

815

if (sessionInfo != null) {

816

sessionInfo.setSessionId(newSessionId);

817

sessionRegistry.put(newSessionId, sessionInfo);

818

}

819

}

820

821

private void logUserLogin(String sessionId, String username) {

822

logger.info("User login: {} (Session: {})", username, sessionId);

823

824

// Update login metrics

825

// In a real application, you might store this in a database

826

}

827

828

private void logUserLogout(String sessionId, String username) {

829

logger.info("User logout: {} (Session: {})", username, sessionId);

830

}

831

832

private void logSessionStatistics(SessionInfo sessionInfo, long duration) {

833

logger.info("Session statistics - ID: {}, Duration: {}ms, Attributes: {}, Passivated: {}",

834

sessionInfo.getSessionId(),

835

duration,

836

sessionInfo.getAttributeCount(),

837

sessionInfo.isPassivated());

838

}

839

840

// Helper class to track session information

841

private static class SessionInfo {

842

private String sessionId;

843

private final long creationTime;

844

private final int maxInactiveInterval;

845

private final Map<String, Object> attributes;

846

private boolean passivated;

847

848

public SessionInfo(String sessionId, long creationTime, int maxInactiveInterval) {

849

this.sessionId = sessionId;

850

this.creationTime = creationTime;

851

this.maxInactiveInterval = maxInactiveInterval;

852

this.attributes = new ConcurrentHashMap<>();

853

this.passivated = false;

854

}

855

856

public String getSessionId() { return sessionId; }

857

public void setSessionId(String sessionId) { this.sessionId = sessionId; }

858

public long getCreationTime() { return creationTime; }

859

public int getMaxInactiveInterval() { return maxInactiveInterval; }

860

public boolean isPassivated() { return passivated; }

861

public void setPassivated(boolean passivated) { this.passivated = passivated; }

862

863

public void addAttribute(String name, Object value) {

864

attributes.put(name, value);

865

}

866

867

public void removeAttribute(String name) {

868

attributes.remove(name);

869

}

870

871

public void updateAttribute(String name, Object value) {

872

attributes.put(name, value);

873

}

874

875

public int getAttributeCount() {

876

return attributes.size();

877

}

878

}

879

}

880

```

881

882

### Request Performance Monitor

883

884

```java { .api }

885

/**

886

* Listener that monitors request performance and resource usage

887

*/

888

@WebListener

889

public class RequestPerformanceMonitor implements ServletRequestListener,

890

ServletRequestAttributeListener {

891

892

private static final Logger logger = LoggerFactory.getLogger(RequestPerformanceMonitor.class);

893

private static final String REQUEST_START_TIME = "request.startTime";

894

private static final AtomicLong requestCounter = new AtomicLong(0);

895

private static final AtomicLong activeRequests = new AtomicLong(0);

896

897

@Override

898

public void requestInitialized(ServletRequestEvent sre) {

899

long requestId = requestCounter.incrementAndGet();

900

long activeCount = activeRequests.incrementAndGet();

901

long startTime = System.currentTimeMillis();

902

903

ServletRequest request = sre.getServletRequest();

904

905

// Store request metadata

906

request.setAttribute("request.id", requestId);

907

request.setAttribute(REQUEST_START_TIME, startTime);

908

request.setAttribute("request.thread", Thread.currentThread().getName());

909

910

if (request instanceof HttpServletRequest) {

911

HttpServletRequest httpRequest = (HttpServletRequest) request;

912

913

logger.debug("Request initialized: {} {} {} (ID: {}, Active: {})",

914

httpRequest.getMethod(),

915

httpRequest.getRequestURI(),

916

httpRequest.getProtocol(),

917

requestId,

918

activeCount);

919

920

// Log detailed request information for monitoring

921

logRequestDetails(httpRequest, requestId);

922

}

923

924

// Update servlet context metrics

925

ServletContext context = sre.getServletContext();

926

context.setAttribute("requests.active.count", activeCount);

927

context.setAttribute("requests.total.count", requestId);

928

}

929

930

@Override

931

public void requestDestroyed(ServletRequestEvent sre) {

932

ServletRequest request = sre.getServletRequest();

933

long activeCount = activeRequests.decrementAndGet();

934

935

Long requestId = (Long) request.getAttribute("request.id");

936

Long startTime = (Long) request.getAttribute(REQUEST_START_TIME);

937

938

if (requestId != null && startTime != null) {

939

long duration = System.currentTimeMillis() - startTime;

940

String threadName = (String) request.getAttribute("request.thread");

941

942

if (request instanceof HttpServletRequest) {

943

HttpServletRequest httpRequest = (HttpServletRequest) request;

944

945

logger.debug("Request destroyed: {} {} (ID: {}, Duration: {}ms, Active: {})",

946

httpRequest.getMethod(),

947

httpRequest.getRequestURI(),

948

requestId,

949

duration,

950

activeCount);

951

952

// Log performance metrics

953

logPerformanceMetrics(httpRequest, requestId, duration, threadName);

954

955

// Check for slow requests

956

if (duration > 5000) { // 5 seconds threshold

957

logger.warn("Slow request detected: {} {} (Duration: {}ms, ID: {})",

958

httpRequest.getMethod(),

959

httpRequest.getRequestURI(),

960

duration,

961

requestId);

962

}

963

}

964

}

965

966

// Update servlet context metrics

967

ServletContext context = sre.getServletContext();

968

context.setAttribute("requests.active.count", activeCount);

969

}

970

971

@Override

972

public void attributeAdded(ServletRequestAttributeEvent srae) {

973

String attributeName = srae.getName();

974

Object attributeValue = srae.getValue();

975

976

// Log important attribute additions

977

if (isImportantAttribute(attributeName)) {

978

Long requestId = (Long) srae.getServletRequest().getAttribute("request.id");

979

logger.debug("Request attribute added: {} = {} (Request ID: {})",

980

attributeName, attributeValue, requestId);

981

}

982

}

983

984

@Override

985

public void attributeRemoved(ServletRequestAttributeEvent srae) {

986

String attributeName = srae.getName();

987

Object attributeValue = srae.getValue();

988

989

// Log important attribute removals

990

if (isImportantAttribute(attributeName)) {

991

Long requestId = (Long) srae.getServletRequest().getAttribute("request.id");

992

logger.debug("Request attribute removed: {} (was: {}) (Request ID: {})",

993

attributeName, attributeValue, requestId);

994

}

995

}

996

997

@Override

998

public void attributeReplaced(ServletRequestAttributeEvent srae) {

999

String attributeName = srae.getName();

1000

Object oldValue = srae.getValue();

1001

Object newValue = srae.getServletRequest().getAttribute(attributeName);

1002

1003

// Log important attribute replacements

1004

if (isImportantAttribute(attributeName)) {

1005

Long requestId = (Long) srae.getServletRequest().getAttribute("request.id");

1006

logger.debug("Request attribute replaced: {} = {} (was: {}) (Request ID: {})",

1007

attributeName, newValue, oldValue, requestId);

1008

}

1009

}

1010

1011

private void logRequestDetails(HttpServletRequest request, long requestId) {

1012

StringBuilder details = new StringBuilder();

1013

details.append("Request Details (ID: ").append(requestId).append(")\n");

1014

details.append(" Method: ").append(request.getMethod()).append("\n");

1015

details.append(" URI: ").append(request.getRequestURI()).append("\n");

1016

details.append(" Query: ").append(request.getQueryString()).append("\n");

1017

details.append(" Remote: ").append(request.getRemoteAddr()).append("\n");

1018

details.append(" User-Agent: ").append(request.getHeader("User-Agent")).append("\n");

1019

details.append(" Content-Type: ").append(request.getContentType()).append("\n");

1020

details.append(" Content-Length: ").append(request.getContentLengthLong()).append("\n");

1021

1022

// Log session info if available

1023

HttpSession session = request.getSession(false);

1024

if (session != null) {

1025

details.append(" Session: ").append(session.getId()).append("\n");

1026

}

1027

1028

logger.trace(details.toString());

1029

}

1030

1031

private void logPerformanceMetrics(HttpServletRequest request, long requestId,

1032

long duration, String threadName) {

1033

1034

// In a real application, you might send these metrics to a monitoring system

1035

StringBuilder metrics = new StringBuilder();

1036

metrics.append("Performance Metrics (ID: ").append(requestId).append(")\n");

1037

metrics.append(" Duration: ").append(duration).append("ms\n");

1038

metrics.append(" Thread: ").append(threadName).append("\n");

1039

metrics.append(" Method: ").append(request.getMethod()).append("\n");

1040

metrics.append(" URI: ").append(request.getRequestURI()).append("\n");

1041

1042

// Memory usage

1043

Runtime runtime = Runtime.getRuntime();

1044

long totalMemory = runtime.totalMemory();

1045

long freeMemory = runtime.freeMemory();

1046

long usedMemory = totalMemory - freeMemory;

1047

1048

metrics.append(" Memory Used: ").append(usedMemory / 1024 / 1024).append("MB\n");

1049

1050

logger.debug(metrics.toString());

1051

1052

// Store metrics for aggregation

1053

storeMetrics(request.getMethod(), request.getRequestURI(), duration);

1054

}

1055

1056

private boolean isImportantAttribute(String attributeName) {

1057

// Define which attributes are important to log

1058

return attributeName.startsWith("user.") ||

1059

attributeName.startsWith("security.") ||

1060

attributeName.startsWith("business.") ||

1061

"authenticated".equals(attributeName) ||

1062

"currentUser".equals(attributeName);

1063

}

1064

1065

private void storeMetrics(String method, String uri, long duration) {

1066

// In a real application, store these metrics in a database or monitoring system

1067

// for analysis and alerting

1068

}

1069

}

1070

```

1071

1072

### Smart Attribute Binding Object

1073

1074

```java { .api }

1075

/**

1076

* Example object that implements HttpSessionBindingListener and

1077

* HttpSessionActivationListener to manage its own lifecycle

1078

*/

1079

public class UserProfile implements HttpSessionBindingListener,

1080

HttpSessionActivationListener,

1081

Serializable {

1082

1083

private static final Logger logger = LoggerFactory.getLogger(UserProfile.class);

1084

private static final long serialVersionUID = 1L;

1085

1086

private String username;

1087

private String email;

1088

private Date lastLoginTime;

1089

private Map<String, Object> preferences;

1090

1091

// Transient fields that will be reinitialized after activation

1092

private transient DatabaseConnection dbConnection;

1093

private transient CacheManager cacheManager;

1094

1095

public UserProfile(String username, String email) {

1096

this.username = username;

1097

this.email = email;

1098

this.lastLoginTime = new Date();

1099

this.preferences = new HashMap<>();

1100

1101

// Initialize transient resources

1102

initializeResources();

1103

}

1104

1105

@Override

1106

public void valueBound(HttpSessionBindingEvent event) {

1107

String sessionId = event.getSession().getId();

1108

logger.info("UserProfile bound to session: {} (User: {})", sessionId, username);

1109

1110

// Record user login

1111

recordUserActivity("LOGIN", sessionId);

1112

1113

// Initialize any session-specific resources

1114

initializeSessionResources(event.getSession());

1115

}

1116

1117

@Override

1118

public void valueUnbound(HttpSessionBindingEvent event) {

1119

String sessionId = event.getSession().getId();

1120

logger.info("UserProfile unbound from session: {} (User: {})", sessionId, username);

1121

1122

// Record user logout

1123

recordUserActivity("LOGOUT", sessionId);

1124

1125

// Cleanup resources

1126

cleanupResources();

1127

}

1128

1129

@Override

1130

public void sessionWillPassivate(HttpSessionEvent se) {

1131

String sessionId = se.getSession().getId();

1132

logger.debug("UserProfile will passivate: {} (User: {})", sessionId, username);

1133

1134

// Save state before passivation

1135

saveUserState();

1136

1137

// Release non-serializable resources

1138

releaseTransientResources();

1139

}

1140

1141

@Override

1142

public void sessionDidActivate(HttpSessionEvent se) {

1143

String sessionId = se.getSession().getId();

1144

logger.debug("UserProfile did activate: {} (User: {})", sessionId, username);

1145

1146

// Reinitialize transient resources

1147

initializeResources();

1148

1149

// Restore user state

1150

restoreUserState();

1151

}

1152

1153

public void updateLastActivity() {

1154

this.lastLoginTime = new Date();

1155

1156

// Update activity in persistent storage

1157

if (dbConnection != null) {

1158

try {

1159

dbConnection.updateLastActivity(username);

1160

} catch (Exception e) {

1161

logger.warn("Failed to update last activity for user: " + username, e);

1162

}

1163

}

1164

}

1165

1166

public void setPreference(String key, Object value) {

1167

preferences.put(key, value);

1168

1169

// Persist preference change

1170

persistPreference(key, value);

1171

}

1172

1173

public Object getPreference(String key) {

1174

return preferences.get(key);

1175

}

1176

1177

private void initializeResources() {

1178

try {

1179

// Initialize database connection

1180

this.dbConnection = DatabaseConnectionFactory.createConnection();

1181

1182

// Initialize cache manager

1183

this.cacheManager = CacheManagerFactory.getInstance();

1184

1185

logger.debug("Transient resources initialized for user: {}", username);

1186

1187

} catch (Exception e) {

1188

logger.error("Failed to initialize resources for user: " + username, e);

1189

}

1190

}

1191

1192

private void initializeSessionResources(HttpSession session) {

1193

// Set up session-specific resources

1194

session.setAttribute("user.loginTime", lastLoginTime);

1195

session.setAttribute("user.preferences.count", preferences.size());

1196

}

1197

1198

private void releaseTransientResources() {

1199

if (dbConnection != null) {

1200

try {

1201

dbConnection.close();

1202

} catch (Exception e) {

1203

logger.warn("Error closing database connection for user: " + username, e);

1204

}

1205

dbConnection = null;

1206

}

1207

1208

cacheManager = null;

1209

1210

logger.debug("Transient resources released for user: {}", username);

1211

}

1212

1213

private void cleanupResources() {

1214

// Release all resources when object is unbound

1215

releaseTransientResources();

1216

1217

// Clear preferences cache

1218

if (cacheManager != null) {

1219

cacheManager.evict("user.preferences." + username);

1220

}

1221

}

1222

1223

private void recordUserActivity(String activity, String sessionId) {

1224

// Record user activity in audit log or analytics system

1225

logger.info("User activity: {} - {} (Session: {})", username, activity, sessionId);

1226

}

1227

1228

private void saveUserState() {

1229

// Save current state to persistent storage before passivation

1230

try {

1231

if (dbConnection != null) {

1232

dbConnection.saveUserPreferences(username, preferences);

1233

dbConnection.updateLastActivity(username);

1234

}

1235

} catch (Exception e) {

1236

logger.error("Failed to save user state for: " + username, e);

1237

}

1238

}

1239

1240

private void restoreUserState() {

1241

// Restore state after activation

1242

try {

1243

if (dbConnection != null) {

1244

Map<String, Object> savedPreferences = dbConnection.loadUserPreferences(username);

1245

if (savedPreferences != null) {

1246

preferences.putAll(savedPreferences);

1247

}

1248

}

1249

} catch (Exception e) {

1250

logger.error("Failed to restore user state for: " + username, e);

1251

}

1252

}

1253

1254

private void persistPreference(String key, Object value) {

1255

// Persist individual preference changes

1256

try {

1257

if (dbConnection != null) {

1258

dbConnection.saveUserPreference(username, key, value);

1259

}

1260

1261

// Update cache

1262

if (cacheManager != null) {

1263

cacheManager.put("user.preference." + username + "." + key, value);

1264

}

1265

} catch (Exception e) {

1266

logger.warn("Failed to persist preference {} for user: {}", key, username, e);

1267

}

1268

}

1269

1270

// Getters and setters

1271

public String getUsername() { return username; }

1272

public String getEmail() { return email; }

1273

public Date getLastLoginTime() { return lastLoginTime; }

1274

public Map<String, Object> getPreferences() { return new HashMap<>(preferences); }

1275

1276

// Mock helper classes (would be real implementations)

1277

private static class DatabaseConnectionFactory {

1278

static DatabaseConnection createConnection() { return new DatabaseConnection(); }

1279

}

1280

1281

private static class DatabaseConnection {

1282

void updateLastActivity(String username) throws Exception {}

1283

void saveUserPreferences(String username, Map<String, Object> preferences) throws Exception {}

1284

Map<String, Object> loadUserPreferences(String username) throws Exception { return null; }

1285

void saveUserPreference(String username, String key, Object value) throws Exception {}

1286

void close() throws Exception {}

1287

}

1288

1289

private static class CacheManagerFactory {

1290

static CacheManager getInstance() { return new CacheManager(); }

1291

}

1292

1293

private static class CacheManager {

1294

void evict(String key) {}

1295

void put(String key, Object value) {}

1296

}

1297

}

1298

```

1299

1300

This comprehensive coverage of listeners and events provides all the tools needed for implementing sophisticated event-driven architectures and monitoring systems in servlet applications, enabling applications to respond intelligently to lifecycle changes and state modifications.