or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

async-processing.mdconfiguration-properties.mdindex.mdmonitoring.mdrequest-processing.mdresource-configuration.mdresource-model.mdspi.mdwadl.md

async-processing.mddocs/

0

# Asynchronous Processing

1

2

Server-side asynchronous request processing including async contexts, chunked output, broadcasting, and managed async execution. Enables scalable handling of long-running operations, streaming responses, and real-time communication patterns.

3

4

## Capabilities

5

6

### AsyncContext

7

8

Server-side asynchronous request processing context providing access to both request and response for asynchronous operations.

9

10

```java { .api }

11

/**

12

* Server-side asynchronous processing context extending AsyncResponse.

13

* Provides access to request and response contexts for asynchronous operations.

14

*/

15

public interface AsyncContext extends AsyncResponse {

16

17

/**

18

* Get the container request associated with this async context.

19

* @return ContainerRequest for the asynchronous operation

20

*/

21

ContainerRequest getContainerRequest();

22

23

/**

24

* Get the container response associated with this async context.

25

* @return ContainerResponse for the asynchronous operation

26

*/

27

ContainerResponse getContainerResponse();

28

29

/**

30

* Suspend the request processing.

31

* @return true if successfully suspended, false if already suspended

32

*/

33

boolean suspend();

34

35

/**

36

* Suspend request processing with timeout.

37

* @param time Timeout value

38

* @param unit Time unit for timeout

39

* @return true if successfully suspended

40

*/

41

boolean suspend(long time, TimeUnit unit);

42

43

/**

44

* Resume processing with a response.

45

* @param response Response to resume with

46

* @return true if successfully resumed

47

*/

48

boolean resume(Object response);

49

50

/**

51

* Resume processing with an exception.

52

* @param throwable Exception to resume with

53

* @return true if successfully resumed

54

*/

55

boolean resume(Throwable throwable);

56

57

/**

58

* Cancel the asynchronous processing.

59

* @return true if successfully cancelled

60

*/

61

boolean cancel();

62

63

/**

64

* Check if the async processing is suspended.

65

* @return true if currently suspended

66

*/

67

boolean isSuspended();

68

69

/**

70

* Check if the async processing is cancelled.

71

* @return true if cancelled

72

*/

73

boolean isCancelled();

74

}

75

```

76

77

**Usage Examples:**

78

79

```java

80

import org.glassfish.jersey.server.AsyncContext;

81

import org.glassfish.jersey.server.ContainerRequest;

82

import jakarta.ws.rs.container.AsyncResponse;

83

import jakarta.ws.rs.container.Suspended;

84

import java.util.concurrent.CompletableFuture;

85

import java.util.concurrent.TimeUnit;

86

87

@Path("/async")

88

public class AsyncResource {

89

90

@GET

91

@Path("/data")

92

public void getDataAsync(@Suspended AsyncResponse asyncResponse) {

93

// Cast to AsyncContext for additional functionality

94

AsyncContext asyncContext = (AsyncContext) asyncResponse;

95

96

// Access request information

97

ContainerRequest request = asyncContext.getContainerRequest();

98

String userAgent = request.getHeaderString("User-Agent");

99

100

// Suspend with timeout

101

asyncContext.suspend(30, TimeUnit.SECONDS);

102

103

// Perform async operation

104

CompletableFuture.supplyAsync(() -> {

105

return performLongRunningOperation();

106

}).thenAccept(result -> {

107

// Resume with result

108

asyncContext.resume(result);

109

}).exceptionally(throwable -> {

110

// Resume with exception

111

asyncContext.resume(throwable);

112

return null;

113

});

114

}

115

116

@POST

117

@Path("/cancel")

118

public String cancelAsync(@QueryParam("id") String operationId) {

119

AsyncContext context = findAsyncContext(operationId);

120

if (context != null && context.isSuspended()) {

121

boolean cancelled = context.cancel();

122

return cancelled ? "Cancelled" : "Could not cancel";

123

}

124

return "Operation not found or not suspended";

125

}

126

}

127

```

128

129

### ChunkedOutput

130

131

Support for chunked transfer encoding responses enabling streaming of data to clients in chunks.

132

133

```java { .api }

134

/**

135

* Chunked output for streaming responses to clients.

136

* Extends GenericType and implements Closeable for resource management.

137

*/

138

public class ChunkedOutput<T> extends GenericType<T> implements Closeable {

139

140

/**

141

* Create chunked output for specified chunk type.

142

* @param chunkType Type of chunks to be written

143

*/

144

public ChunkedOutput(Type chunkType);

145

146

/**

147

* Create chunked output for specified chunk type with separator.

148

* @param chunkType Type of chunks to be written

149

* @param separator Separator between chunks

150

*/

151

public ChunkedOutput(Type chunkType, String separator);

152

153

/**

154

* Check if the chunked output is closed.

155

* @return true if closed, false otherwise

156

*/

157

public boolean isClosed();

158

159

/**

160

* Write a chunk to the output.

161

* @param chunk Chunk data to write

162

* @throws IOException if writing fails or output is closed

163

*/

164

public void write(T chunk) throws IOException;

165

166

/**

167

* Close the chunked output stream.

168

* @throws IOException if closing fails

169

*/

170

public void close() throws IOException;

171

172

/**

173

* Set exception callback for error handling.

174

* @param callback Callback to handle exceptions

175

*/

176

public void setExceptionCallback(ChunkedOutput.ExceptionCallback callback);

177

178

/**

179

* Exception callback interface for handling write errors.

180

*/

181

public interface ExceptionCallback {

182

void onException(ChunkedOutput chunkedOutput, Exception exception);

183

}

184

}

185

```

186

187

**Usage Examples:**

188

189

```java

190

import org.glassfish.jersey.server.ChunkedOutput;

191

import jakarta.ws.rs.GET;

192

import jakarta.ws.rs.Path;

193

import jakarta.ws.rs.Produces;

194

import jakarta.ws.rs.core.MediaType;

195

import java.io.IOException;

196

import java.util.concurrent.Executors;

197

import java.util.concurrent.ScheduledExecutorService;

198

import java.util.concurrent.TimeUnit;

199

200

@Path("/stream")

201

public class StreamingResource {

202

203

private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(5);

204

205

@GET

206

@Path("/data")

207

@Produces(MediaType.APPLICATION_JSON)

208

public ChunkedOutput<String> streamData() {

209

ChunkedOutput<String> output = new ChunkedOutput<>(String.class);

210

211

// Set exception callback

212

output.setExceptionCallback((chunkedOutput, exception) -> {

213

System.err.println("Streaming error: " + exception.getMessage());

214

});

215

216

// Stream data every second for 10 seconds

217

executor.scheduleAtFixedRate(() -> {

218

try {

219

if (!output.isClosed()) {

220

String data = generateData();

221

output.write(data);

222

}

223

} catch (IOException e) {

224

try {

225

output.close();

226

} catch (IOException closeEx) {

227

// Log close exception

228

}

229

}

230

}, 0, 1, TimeUnit.SECONDS);

231

232

// Auto-close after 10 seconds

233

executor.schedule(() -> {

234

try {

235

output.close();

236

} catch (IOException e) {

237

// Log exception

238

}

239

}, 10, TimeUnit.SECONDS);

240

241

return output;

242

}

243

244

@GET

245

@Path("/logs")

246

@Produces(MediaType.TEXT_PLAIN)

247

public ChunkedOutput<String> streamLogs() {

248

ChunkedOutput<String> output = new ChunkedOutput<>(String.class, "\n");

249

250

// Stream log entries

251

streamLogEntries(output);

252

253

return output;

254

}

255

256

private String generateData() {

257

return "{\"timestamp\":" + System.currentTimeMillis() + ",\"data\":\"example\"}";

258

}

259

}

260

```

261

262

### Broadcaster

263

264

Broadcasting utility for sending messages to multiple clients simultaneously, useful for real-time notifications and pub-sub patterns.

265

266

```java { .api }

267

/**

268

* Broadcaster for sending messages to multiple ChunkedOutput instances.

269

* Implements BroadcasterListener for lifecycle management.

270

*/

271

public final class Broadcaster<T> implements BroadcasterListener<T> {

272

273

/**

274

* Create a broadcaster for specific chunk type without automatic closing.

275

* @param chunkType Type of chunks to broadcast

276

* @return Broadcaster instance

277

*/

278

public static <T> Broadcaster<T> createOnly(Class<T> chunkType);

279

280

/**

281

* Create a broadcaster for specific chunk type with automatic closing.

282

* @param chunkType Type of chunks to broadcast

283

* @return Broadcaster instance that auto-closes when empty

284

*/

285

public static <T> Broadcaster<T> create(Class<T> chunkType);

286

287

/**

288

* Add a chunked output to the broadcaster.

289

* @param chunkedOutput ChunkedOutput to add

290

* @return true if successfully added

291

*/

292

public boolean add(ChunkedOutput<T> chunkedOutput);

293

294

/**

295

* Remove a chunked output from the broadcaster.

296

* @param chunkedOutput ChunkedOutput to remove

297

* @return true if successfully removed

298

*/

299

public boolean remove(ChunkedOutput<T> chunkedOutput);

300

301

/**

302

* Broadcast a chunk to all registered outputs.

303

* @param chunk Chunk to broadcast

304

*/

305

public void broadcast(T chunk);

306

307

/**

308

* Close all registered outputs and clear the broadcaster.

309

*/

310

public void closeAll();

311

312

/**

313

* Get the number of registered outputs.

314

* @return Number of active chunked outputs

315

*/

316

public int size();

317

318

/**

319

* Check if the broadcaster is empty.

320

* @return true if no outputs are registered

321

*/

322

public boolean isEmpty();

323

324

/**

325

* Add a broadcaster listener.

326

* @param listener Listener to add

327

*/

328

public void addListener(BroadcasterListener<T> listener);

329

330

/**

331

* Remove a broadcaster listener.

332

* @param listener Listener to remove

333

*/

334

public void removeListener(BroadcasterListener<T> listener);

335

}

336

```

337

338

**Usage Examples:**

339

340

```java

341

import org.glassfish.jersey.server.Broadcaster;

342

import org.glassfish.jersey.server.ChunkedOutput;

343

import org.glassfish.jersey.server.BroadcasterListener;

344

import jakarta.ws.rs.GET;

345

import jakarta.ws.rs.POST;

346

import jakarta.ws.rs.Path;

347

import jakarta.ws.rs.Produces;

348

import jakarta.ws.rs.core.MediaType;

349

350

@Path("/notifications")

351

public class NotificationResource {

352

353

// Static broadcaster for notifications

354

private static final Broadcaster<String> broadcaster = Broadcaster.create(String.class);

355

356

static {

357

// Add listener for broadcaster events

358

broadcaster.addListener(new BroadcasterListener<String>() {

359

@Override

360

public void onClose(ChunkedOutput<String> chunkedOutput) {

361

System.out.println("Client disconnected from notifications");

362

}

363

364

@Override

365

public void onException(ChunkedOutput<String> chunkedOutput, Exception exception) {

366

System.err.println("Broadcast error: " + exception.getMessage());

367

}

368

});

369

}

370

371

@GET

372

@Path("/subscribe")

373

@Produces(MediaType.TEXT_PLAIN)

374

public ChunkedOutput<String> subscribe() {

375

ChunkedOutput<String> output = new ChunkedOutput<>(String.class);

376

377

// Add to broadcaster

378

broadcaster.add(output);

379

380

// Send welcome message

381

try {

382

output.write("Connected to notifications");

383

} catch (IOException e) {

384

broadcaster.remove(output);

385

}

386

387

return output;

388

}

389

390

@POST

391

@Path("/send")

392

public String sendNotification(String message) {

393

// Broadcast to all subscribers

394

broadcaster.broadcast("NOTIFICATION: " + message);

395

396

return "Notification sent to " + broadcaster.size() + " subscribers";

397

}

398

399

@GET

400

@Path("/stats")

401

@Produces(MediaType.APPLICATION_JSON)

402

public String getStats() {

403

return "{\"subscribers\":" + broadcaster.size() + ",\"isEmpty\":" + broadcaster.isEmpty() + "}";

404

}

405

406

@POST

407

@Path("/shutdown")

408

public String shutdown() {

409

broadcaster.closeAll();

410

return "All notification subscribers disconnected";

411

}

412

}

413

```

414

415

### BroadcasterListener

416

417

Event listener interface for broadcaster lifecycle events and error handling.

418

419

```java { .api }

420

/**

421

* Listener interface for broadcaster events.

422

* Provides callbacks for output lifecycle and error handling.

423

*/

424

public interface BroadcasterListener<T> {

425

426

/**

427

* Called when a chunked output is closed.

428

* @param chunkedOutput ChunkedOutput that was closed

429

*/

430

default void onClose(ChunkedOutput<T> chunkedOutput) {

431

// Default implementation does nothing

432

}

433

434

/**

435

* Called when an exception occurs during broadcasting.

436

* @param chunkedOutput ChunkedOutput where exception occurred

437

* @param exception Exception that occurred

438

*/

439

default void onException(ChunkedOutput<T> chunkedOutput, Exception exception) {

440

// Default implementation does nothing

441

}

442

}

443

```

444

445

### Managed Async Annotations

446

447

Annotations for controlling managed asynchronous execution in Jersey resources.

448

449

```java { .api }

450

/**

451

* Annotation marking methods for managed asynchronous execution.

452

* Methods annotated with this will be executed on a managed thread pool.

453

*/

454

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

455

@Retention(RetentionPolicy.RUNTIME)

456

public @interface ManagedAsync {

457

// Marker annotation - no parameters

458

}

459

460

/**

461

* Qualifier annotation for injecting managed async executor.

462

* Used with @Inject to get the managed executor service.

463

*/

464

@Qualifier

465

@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})

466

@Retention(RetentionPolicy.RUNTIME)

467

public @interface ManagedAsyncExecutor {

468

// Qualifier annotation - no parameters

469

}

470

471

/**

472

* Qualifier annotation for injecting background scheduler.

473

* Used with @Inject to get the background scheduled executor service.

474

*/

475

@Qualifier

476

@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})

477

@Retention(RetentionPolicy.RUNTIME)

478

public @interface BackgroundScheduler {

479

// Qualifier annotation - no parameters

480

}

481

```

482

483

**Usage Examples:**

484

485

```java

486

import org.glassfish.jersey.server.ManagedAsync;

487

import org.glassfish.jersey.server.ManagedAsyncExecutor;

488

import org.glassfish.jersey.server.BackgroundScheduler;

489

import jakarta.inject.Inject;

490

import jakarta.ws.rs.GET;

491

import jakarta.ws.rs.Path;

492

import jakarta.ws.rs.container.AsyncResponse;

493

import jakarta.ws.rs.container.Suspended;

494

import java.util.concurrent.ScheduledExecutorService;

495

import java.util.concurrent.ExecutorService;

496

import java.util.concurrent.TimeUnit;

497

498

@Path("/managed")

499

public class ManagedAsyncResource {

500

501

@Inject

502

@ManagedAsyncExecutor

503

private ExecutorService asyncExecutor;

504

505

@Inject

506

@BackgroundScheduler

507

private ScheduledExecutorService scheduler;

508

509

@GET

510

@Path("/simple")

511

@ManagedAsync

512

public String getManagedAsync() {

513

// This method will be executed on managed thread pool

514

return performLongOperation();

515

}

516

517

@GET

518

@Path("/custom")

519

public void getCustomAsync(@Suspended AsyncResponse asyncResponse) {

520

// Use injected managed executor

521

asyncExecutor.submit(() -> {

522

try {

523

String result = performLongOperation();

524

asyncResponse.resume(result);

525

} catch (Exception e) {

526

asyncResponse.resume(e);

527

}

528

});

529

}

530

531

@GET

532

@Path("/scheduled")

533

public void getScheduledAsync(@Suspended AsyncResponse asyncResponse) {

534

// Use injected background scheduler

535

scheduler.schedule(() -> {

536

try {

537

String result = performDelayedOperation();

538

asyncResponse.resume(result);

539

} catch (Exception e) {

540

asyncResponse.resume(e);

541

}

542

}, 5, TimeUnit.SECONDS);

543

}

544

545

@ManagedAsync

546

@GET

547

@Path("/streaming")

548

public ChunkedOutput<String> getManagedStreaming() {

549

// Managed async with streaming

550

ChunkedOutput<String> output = new ChunkedOutput<>(String.class);

551

552

scheduler.scheduleAtFixedRate(() -> {

553

try {

554

if (!output.isClosed()) {

555

output.write("Managed stream data: " + System.currentTimeMillis());

556

}

557

} catch (IOException e) {

558

try {

559

output.close();

560

} catch (IOException closeEx) {

561

// Log close exception

562

}

563

}

564

}, 0, 2, TimeUnit.SECONDS);

565

566

return output;

567

}

568

569

private String performLongOperation() {

570

// Simulate long-running operation

571

try {

572

Thread.sleep(2000);

573

} catch (InterruptedException e) {

574

Thread.currentThread().interrupt();

575

}

576

return "Long operation completed";

577

}

578

579

private String performDelayedOperation() {

580

return "Delayed operation completed at " + System.currentTimeMillis();

581

}

582

}

583

```