or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

base-filtering.mdcors-filter.mddos-protection.mdheader-management.mdindex.mdquality-of-service.mdserver-sent-events.md

server-sent-events.mddocs/

0

# Server-Sent Events

1

2

Implementation of the W3C EventSource specification for server-sent events, enabling real-time server-to-client communication. This provides a standardized way to push data from server to web clients using HTTP connections.

3

4

## Capabilities

5

6

### EventSource Interface

7

8

Core interface for handling server-sent event connections.

9

10

```java { .api }

11

/**

12

* Passive half of an event source connection as defined by the EventSource specification.

13

* Allows applications to be notified of connection events and handle the connection lifecycle.

14

*/

15

public interface EventSource {

16

/**

17

* Callback invoked when an event source connection is opened

18

* @param emitter The Emitter instance for operating on the connection

19

* @throws IOException if the implementation throws such exception

20

*/

21

void onOpen(Emitter emitter) throws IOException;

22

23

/**

24

* Callback invoked when an event source connection is closed

25

*/

26

void onClose();

27

}

28

```

29

30

### Emitter Interface

31

32

Active interface for sending events to the client.

33

34

```java { .api }

35

/**

36

* Active half of an event source connection, allows applications to operate on the connection

37

* by sending events, data, comments, or closing the connection.

38

* Instances are fully thread-safe and can be used from multiple threads.

39

*/

40

public interface Emitter {

41

/**

42

* Send a named event with data to the client

43

* @param name The event name

44

* @param data The data to be sent

45

* @throws IOException if an I/O failure occurred

46

*/

47

void event(String name, String data) throws IOException;

48

49

/**

50

* Send a default event with data to the client

51

* Multi-line data is automatically split into multiple data lines

52

* @param data The data to be sent

53

* @throws IOException if an I/O failure occurred

54

*/

55

void data(String data) throws IOException;

56

57

/**

58

* Send a comment to the client

59

* @param comment The comment to send

60

* @throws IOException if an I/O failure occurred

61

*/

62

void comment(String comment) throws IOException;

63

64

/**

65

* Close this event source connection

66

*/

67

void close();

68

}

69

```

70

71

### EventSourceServlet

72

73

Abstract servlet that implements the event source protocol.

74

75

```java { .api }

76

/**

77

* Servlet implementing the event source protocol (server-sent events).

78

* Must be subclassed to implement newEventSource method.

79

*/

80

public abstract class EventSourceServlet extends HttpServlet {

81

/**

82

* Initialize the servlet, set up heartbeat configuration

83

* @throws ServletException if initialization fails

84

*/

85

public void init() throws ServletException;

86

87

/**

88

* Clean up resources when servlet is destroyed

89

*/

90

public void destroy();

91

92

/**

93

* Create an EventSource instance for the given request.

94

* Must be implemented by subclasses.

95

* @param request The HTTP request

96

* @return EventSource instance or null to send 503 error

97

*/

98

protected abstract EventSource newEventSource(HttpServletRequest request);

99

100

/**

101

* Set up the HTTP response for event streaming

102

* @param request The HTTP request

103

* @param response The HTTP response

104

* @throws IOException if response setup fails

105

*/

106

protected void respond(HttpServletRequest request, HttpServletResponse response) throws IOException;

107

108

/**

109

* Handle the opening of an event source connection

110

* @param eventSource The EventSource instance

111

* @param emitter The Emitter for the connection

112

* @throws IOException if connection setup fails

113

*/

114

protected void open(EventSource eventSource, EventSource.Emitter emitter) throws IOException;

115

}

116

```

117

118

**Configuration Parameters:**

119

120

- **heartBeatPeriod**: Heartbeat period in seconds for checking closed connections (default: 10)

121

122

### EventSourceEmitter

123

124

Internal implementation of the Emitter interface.

125

126

```java { .api }

127

/**

128

* Implementation of EventSource.Emitter that handles the actual event streaming

129

*/

130

public class EventSourceEmitter implements EventSource.Emitter, Runnable {

131

/**

132

* Create a new emitter for the given EventSource and AsyncContext

133

* @param eventSource The EventSource instance

134

* @param async The AsyncContext for the connection

135

* @throws IOException if emitter creation fails

136

*/

137

public EventSourceEmitter(EventSource eventSource, AsyncContext async) throws IOException;

138

}

139

```

140

141

## Usage Examples

142

143

### Basic EventSource Implementation

144

145

```java

146

import org.eclipse.jetty.ee10.servlets.EventSource;

147

import org.eclipse.jetty.ee10.servlets.EventSourceServlet;

148

import jakarta.servlet.http.HttpServletRequest;

149

import java.io.IOException;

150

import java.util.concurrent.ScheduledExecutorService;

151

import java.util.concurrent.Executors;

152

import java.util.concurrent.TimeUnit;

153

154

public class TimeEventSourceServlet extends EventSourceServlet {

155

private ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();

156

157

@Override

158

protected EventSource newEventSource(HttpServletRequest request) {

159

return new EventSource() {

160

@Override

161

public void onOpen(Emitter emitter) throws IOException {

162

// Send welcome message

163

emitter.data("Connection established");

164

165

// Send current time every 5 seconds

166

executor.scheduleAtFixedRate(() -> {

167

try {

168

emitter.event("time", new java.util.Date().toString());

169

} catch (IOException e) {

170

emitter.close();

171

}

172

}, 0, 5, TimeUnit.SECONDS);

173

}

174

175

@Override

176

public void onClose() {

177

// Cleanup when client disconnects

178

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

179

}

180

};

181

}

182

183

@Override

184

public void destroy() {

185

super.destroy();

186

executor.shutdown();

187

}

188

}

189

```

190

191

### Chat Room EventSource

192

193

```java

194

import org.eclipse.jetty.ee10.servlets.EventSource;

195

import org.eclipse.jetty.ee10.servlets.EventSourceServlet;

196

import jakarta.servlet.http.HttpServletRequest;

197

import java.io.IOException;

198

import java.util.concurrent.CopyOnWriteArrayList;

199

import java.util.List;

200

201

public class ChatEventSourceServlet extends EventSourceServlet {

202

private static final List<EventSource.Emitter> clients = new CopyOnWriteArrayList<>();

203

204

@Override

205

protected EventSource newEventSource(HttpServletRequest request) {

206

final String username = request.getParameter("username");

207

208

return new EventSource() {

209

private EventSource.Emitter myEmitter;

210

211

@Override

212

public void onOpen(Emitter emitter) throws IOException {

213

myEmitter = emitter;

214

clients.add(emitter);

215

216

// Welcome message to new client

217

emitter.data("Welcome to the chat, " + username + "!");

218

219

// Notify other clients

220

broadcastMessage("system", username + " joined the chat");

221

}

222

223

@Override

224

public void onClose() {

225

clients.remove(myEmitter);

226

broadcastMessage("system", username + " left the chat");

227

}

228

};

229

}

230

231

private void broadcastMessage(String type, String message) {

232

clients.removeIf(emitter -> {

233

try {

234

emitter.event(type, message);

235

return false; // Keep in list

236

} catch (IOException e) {

237

emitter.close();

238

return true; // Remove from list

239

}

240

});

241

}

242

243

// Method to send messages from other servlets/endpoints

244

public static void sendChatMessage(String username, String message) {

245

ChatEventSourceServlet servlet = new ChatEventSourceServlet();

246

servlet.broadcastMessage("message", username + ": " + message);

247

}

248

}

249

```

250

251

### News Feed EventSource

252

253

```java

254

public class NewsFeedEventSourceServlet extends EventSourceServlet {

255

@Override

256

protected EventSource newEventSource(HttpServletRequest request) {

257

final String category = request.getParameter("category");

258

259

return new EventSource() {

260

@Override

261

public void onOpen(Emitter emitter) throws IOException {

262

// Send initial data

263

emitter.comment("News feed started for category: " + category);

264

265

// Send recent articles

266

List<Article> recentArticles = getRecentArticles(category);

267

for (Article article : recentArticles) {

268

String articleData = String.format(

269

"{\"title\":\"%s\",\"url\":\"%s\",\"timestamp\":\"%s\"}",

270

article.getTitle(),

271

article.getUrl(),

272

article.getTimestamp()

273

);

274

emitter.event("article", articleData);

275

}

276

277

// Register for live updates

278

NewsService.registerListener(category, (article) -> {

279

try {

280

String articleData = String.format(

281

"{\"title\":\"%s\",\"url\":\"%s\",\"timestamp\":\"%s\"}",

282

article.getTitle(),

283

article.getUrl(),

284

article.getTimestamp()

285

);

286

emitter.event("new-article", articleData);

287

} catch (IOException e) {

288

emitter.close();

289

}

290

});

291

}

292

293

@Override

294

public void onClose() {

295

NewsService.unregisterListener(category);

296

}

297

};

298

}

299

300

private List<Article> getRecentArticles(String category) {

301

// Implementation to fetch recent articles

302

return NewsService.getRecentArticles(category, 10);

303

}

304

}

305

```

306

307

## Web.xml Configuration

308

309

```xml

310

<servlet>

311

<servlet-name>EventSourceServlet</servlet-name>

312

<servlet-class>com.example.MyEventSourceServlet</servlet-class>

313

<init-param>

314

<param-name>heartBeatPeriod</param-name>

315

<param-value>30</param-value>

316

</init-param>

317

</servlet>

318

<servlet-mapping>

319

<servlet-name>EventSourceServlet</servlet-name>

320

<url-pattern>/events/*</url-pattern>

321

</servlet-mapping>

322

```

323

324

## Client-Side JavaScript

325

326

```javascript

327

// Basic client connection

328

const eventSource = new EventSource('/events');

329

330

eventSource.onmessage = function(event) {

331

console.log('Data received:', event.data);

332

};

333

334

eventSource.addEventListener('time', function(event) {

335

console.log('Time event:', event.data);

336

});

337

338

eventSource.onerror = function(event) {

339

console.log('Error occurred:', event);

340

};

341

342

// Close connection

343

eventSource.close();

344

```

345

346

## Thread Safety

347

348

The `EventSource.Emitter` instances are fully thread-safe and can be used from multiple threads simultaneously. This allows for complex event publishing scenarios where multiple background threads may need to send events to connected clients.

349

350

## Connection Management

351

352

- Connections are automatically cleaned up when clients disconnect

353

- Heartbeat mechanism detects stale connections (configurable via `heartBeatPeriod`)

354

- The servlet uses async processing to handle multiple concurrent connections efficiently

355

- Failed write operations automatically close the connection and trigger `onClose()` callback