CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-eclipse-jetty-ee10--jetty-ee10-servlets

Collection of utility servlets and filters for Jakarta EE 10 web applications including CORS, DoS protection, QoS management, header manipulation, and server-sent events.

Pending
Overview
Eval results
Files

server-sent-events.mddocs/

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.

Capabilities

EventSource Interface

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();
}

Emitter Interface

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();
}

EventSourceServlet

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:

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

EventSourceEmitter

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;
}

Usage Examples

Basic EventSource Implementation

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();
    }
}

Chat Room EventSource

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);
    }
}

News Feed EventSource

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);
    }
}

Web.xml Configuration

<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>

Client-Side JavaScript

// 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();

Thread Safety

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.

Connection Management

  • Connections are automatically cleaned up when clients disconnect
  • Heartbeat mechanism detects stale connections (configurable via heartBeatPeriod)
  • The servlet uses async processing to handle multiple concurrent connections efficiently
  • Failed write operations automatically close the connection and trigger onClose() callback

Install with Tessl CLI

npx tessl i tessl/maven-org-eclipse-jetty-ee10--jetty-ee10-servlets

docs

base-filtering.md

cors-filter.md

dos-protection.md

header-management.md

index.md

quality-of-service.md

server-sent-events.md

tile.json