Collection of utility servlets and filters for Jakarta EE 10 web applications including CORS, DoS protection, QoS management, header manipulation, and server-sent events.
—
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.
Core interface for handling server-sent event connections.
/**
* Passive half of an event source connection as defined by the EventSource specification.
* Allows applications to be notified of connection events and handle the connection lifecycle.
*/
public interface EventSource {
/**
* Callback invoked when an event source connection is opened
* @param emitter The Emitter instance for operating on the connection
* @throws IOException if the implementation throws such exception
*/
void onOpen(Emitter emitter) throws IOException;
/**
* Callback invoked when an event source connection is closed
*/
void onClose();
}Active interface for sending events to the client.
/**
* Active half of an event source connection, allows applications to operate on the connection
* by sending events, data, comments, or closing the connection.
* Instances are fully thread-safe and can be used from multiple threads.
*/
public interface Emitter {
/**
* Send a named event with data to the client
* @param name The event name
* @param data The data to be sent
* @throws IOException if an I/O failure occurred
*/
void event(String name, String data) throws IOException;
/**
* Send a default event with data to the client
* Multi-line data is automatically split into multiple data lines
* @param data The data to be sent
* @throws IOException if an I/O failure occurred
*/
void data(String data) throws IOException;
/**
* Send a comment to the client
* @param comment The comment to send
* @throws IOException if an I/O failure occurred
*/
void comment(String comment) throws IOException;
/**
* Close this event source connection
*/
void close();
}Abstract servlet that implements the event source protocol.
/**
* Servlet implementing the event source protocol (server-sent events).
* Must be subclassed to implement newEventSource method.
*/
public abstract class EventSourceServlet extends HttpServlet {
/**
* Initialize the servlet, set up heartbeat configuration
* @throws ServletException if initialization fails
*/
public void init() throws ServletException;
/**
* Clean up resources when servlet is destroyed
*/
public void destroy();
/**
* Create an EventSource instance for the given request.
* Must be implemented by subclasses.
* @param request The HTTP request
* @return EventSource instance or null to send 503 error
*/
protected abstract EventSource newEventSource(HttpServletRequest request);
/**
* Set up the HTTP response for event streaming
* @param request The HTTP request
* @param response The HTTP response
* @throws IOException if response setup fails
*/
protected void respond(HttpServletRequest request, HttpServletResponse response) throws IOException;
/**
* Handle the opening of an event source connection
* @param eventSource The EventSource instance
* @param emitter The Emitter for the connection
* @throws IOException if connection setup fails
*/
protected void open(EventSource eventSource, EventSource.Emitter emitter) throws IOException;
}Configuration Parameters:
Internal implementation of the Emitter interface.
/**
* Implementation of EventSource.Emitter that handles the actual event streaming
*/
public class EventSourceEmitter implements EventSource.Emitter, Runnable {
/**
* Create a new emitter for the given EventSource and AsyncContext
* @param eventSource The EventSource instance
* @param async The AsyncContext for the connection
* @throws IOException if emitter creation fails
*/
public EventSourceEmitter(EventSource eventSource, AsyncContext async) throws IOException;
}import org.eclipse.jetty.ee10.servlets.EventSource;
import org.eclipse.jetty.ee10.servlets.EventSourceServlet;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class TimeEventSourceServlet extends EventSourceServlet {
private ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
@Override
protected EventSource newEventSource(HttpServletRequest request) {
return new EventSource() {
@Override
public void onOpen(Emitter emitter) throws IOException {
// Send welcome message
emitter.data("Connection established");
// Send current time every 5 seconds
executor.scheduleAtFixedRate(() -> {
try {
emitter.event("time", new java.util.Date().toString());
} catch (IOException e) {
emitter.close();
}
}, 0, 5, TimeUnit.SECONDS);
}
@Override
public void onClose() {
// Cleanup when client disconnects
System.out.println("Client disconnected");
}
};
}
@Override
public void destroy() {
super.destroy();
executor.shutdown();
}
}import org.eclipse.jetty.ee10.servlets.EventSource;
import org.eclipse.jetty.ee10.servlets.EventSourceServlet;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.List;
public class ChatEventSourceServlet extends EventSourceServlet {
private static final List<EventSource.Emitter> clients = new CopyOnWriteArrayList<>();
@Override
protected EventSource newEventSource(HttpServletRequest request) {
final String username = request.getParameter("username");
return new EventSource() {
private EventSource.Emitter myEmitter;
@Override
public void onOpen(Emitter emitter) throws IOException {
myEmitter = emitter;
clients.add(emitter);
// Welcome message to new client
emitter.data("Welcome to the chat, " + username + "!");
// Notify other clients
broadcastMessage("system", username + " joined the chat");
}
@Override
public void onClose() {
clients.remove(myEmitter);
broadcastMessage("system", username + " left the chat");
}
};
}
private void broadcastMessage(String type, String message) {
clients.removeIf(emitter -> {
try {
emitter.event(type, message);
return false; // Keep in list
} catch (IOException e) {
emitter.close();
return true; // Remove from list
}
});
}
// Method to send messages from other servlets/endpoints
public static void sendChatMessage(String username, String message) {
ChatEventSourceServlet servlet = new ChatEventSourceServlet();
servlet.broadcastMessage("message", username + ": " + message);
}
}public class NewsFeedEventSourceServlet extends EventSourceServlet {
@Override
protected EventSource newEventSource(HttpServletRequest request) {
final String category = request.getParameter("category");
return new EventSource() {
@Override
public void onOpen(Emitter emitter) throws IOException {
// Send initial data
emitter.comment("News feed started for category: " + category);
// Send recent articles
List<Article> recentArticles = getRecentArticles(category);
for (Article article : recentArticles) {
String articleData = String.format(
"{\"title\":\"%s\",\"url\":\"%s\",\"timestamp\":\"%s\"}",
article.getTitle(),
article.getUrl(),
article.getTimestamp()
);
emitter.event("article", articleData);
}
// Register for live updates
NewsService.registerListener(category, (article) -> {
try {
String articleData = String.format(
"{\"title\":\"%s\",\"url\":\"%s\",\"timestamp\":\"%s\"}",
article.getTitle(),
article.getUrl(),
article.getTimestamp()
);
emitter.event("new-article", articleData);
} catch (IOException e) {
emitter.close();
}
});
}
@Override
public void onClose() {
NewsService.unregisterListener(category);
}
};
}
private List<Article> getRecentArticles(String category) {
// Implementation to fetch recent articles
return NewsService.getRecentArticles(category, 10);
}
}<servlet>
<servlet-name>EventSourceServlet</servlet-name>
<servlet-class>com.example.MyEventSourceServlet</servlet-class>
<init-param>
<param-name>heartBeatPeriod</param-name>
<param-value>30</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>EventSourceServlet</servlet-name>
<url-pattern>/events/*</url-pattern>
</servlet-mapping>// Basic client connection
const eventSource = new EventSource('/events');
eventSource.onmessage = function(event) {
console.log('Data received:', event.data);
};
eventSource.addEventListener('time', function(event) {
console.log('Time event:', event.data);
});
eventSource.onerror = function(event) {
console.log('Error occurred:', event);
};
// Close connection
eventSource.close();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.
heartBeatPeriod)onClose() callbackInstall with Tessl CLI
npx tessl i tessl/maven-org-eclipse-jetty-ee10--jetty-ee10-servlets