All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javax.ws.rs.sse.SseEventSource Maven / Gradle / Ivy

Go to download

A bundle project producing JAX-RS RI bundles. The primary artifact is an "all-in-one" OSGi-fied JAX-RS RI bundle (jaxrs-ri.jar). Attached to that are two compressed JAX-RS RI archives. The first archive (jaxrs-ri.zip) consists of binary RI bits and contains the API jar (under "api" directory), RI libraries (under "lib" directory) as well as all external RI dependencies (under "ext" directory). The secondary archive (jaxrs-ri-src.zip) contains buildable JAX-RS RI source bundle and contains the API jar (under "api" directory), RI sources (under "src" directory) as well as all external RI dependencies (under "ext" directory). The second archive also contains "build.xml" ANT script that builds the RI sources. To build the JAX-RS RI simply unzip the archive, cd to the created jaxrs-ri directory and invoke "ant" from the command line.

There is a newer version: 3.1.9
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * http://glassfish.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */
package javax.ws.rs.sse;

import java.net.URL;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

import javax.ws.rs.Flow;
import javax.ws.rs.client.WebTarget;

/**
 * Client for reading and processing {@link InboundSseEvent incoming Server-Sent Events}.
 * 

* SSE event source instances of this class are thread safe. To build a new instance, you can use the * {@link #target(javax.ws.rs.client.WebTarget) SseEventSource.target(endpoint)} factory method to get * a new event source builder that can be further customised and eventually used to create a new SSE * event source. *

* Once a {@link SseEventSource} is created, it can be used to {@link #open open a connection} * to the associated {@link WebTarget web target}. After establishing the connection, the event source starts * processing any incoming inbound events. Whenever a new event is received, an * {@link Consumer#accept(Object) Consumer#accept(InboundSseEvent)} method is invoked on any registered event consumers. *

Reconnect support

*

* The {@code SseEventSource} supports automated recuperation from a connection loss, including * negotiation of delivery of any missed events based on the last received SSE event {@code id} field value, provided * this field is set by the server and the negotiation facility is supported by the server. In case of a connection loss, * the last received SSE event {@code id} field value is send in the * {@value javax.ws.rs.core.HttpHeaders#LAST_EVENT_ID_HEADER} HTTP * request header as part of a new connection request sent to the SSE endpoint. Upon a receipt of such reconnect request, the SSE * endpoint that supports this negotiation facility is expected to replay all missed events. Note however, that this is a * best-effort mechanism which does not provide any guaranty that all events would be delivered without a loss. You should * therefore not rely on receiving every single event and design your client application code accordingly. *

* By default, when a connection the the SSE endpoint is lost, the event source will wait 500 ms * before attempting to reconnect to the SSE endpoint. The SSE endpoint can however control the client-side retry delay * by including a special {@code retry} field value in the any send event. JAX-RS {@code SseEventSource} tracks any * received SSE event {@code retry} field values set by the endpoint and adjusts the reconnect delay accordingly, * using the last received {@code retry} field value as the reconnect delay. *

* In addition to handling the standard connection loss failures, JAX-RS {@code SseEventSource} automatically deals with any * {@code HTTP 503 Service Unavailable} responses from an SSE endpoint, that contain a * {@value javax.ws.rs.core.HttpHeaders#RETRY_AFTER} HTTP header with a valid value. The * HTTP 503 + {@value javax.ws.rs.core.HttpHeaders#RETRY_AFTER} technique is often used by HTTP endpoints * as a means of connection and traffic throttling. * In case a HTTP 503 + {@value javax.ws.rs.core.HttpHeaders#RETRY_AFTER} response is received in return to a connection * request, JAX-RS SSE event source will automatically schedule a new reconnect attempt and use the received * {@value javax.ws.rs.core.HttpHeaders#RETRY_AFTER} HTTP header value as a one-time override of the reconnect delay. * * @author Marek Potociar * @since 2.1 */ public interface SseEventSource extends AutoCloseable /*, Flow.Publisher */ { /** * JAX-RS {@link SseEventSource} builder class. *

* Event source builder provides methods that let you conveniently configure and subsequently build * a new {@code SseEventSource} instance. You can obtain a new event source builder instance using * a static {@link SseEventSource#target(javax.ws.rs.client.WebTarget) SseEventSource.target(endpoint)} factory method. *

* For example: *

     * SseEventSource es = SseEventSource.target(endpoint).named("my source")
     *                             .reconnectingEvery(5, SECONDS)
     *                             .build();
     * es.subscribe(System.out::println);
     * es.open();
     * 
*/ abstract class Builder { /** * Name of the property identifying the {@link SseEventSource.Builder} implementation * to be returned from {@link SseEventSource.Builder#newBuilder()}. */ public static final String JAXRS_DEFAULT_SSE_BUILDER_PROPERTY = "javax.ws.rs.sse.SseEventSource.Builder"; /** * Default SSE event source builder implementation class name. */ private static final String JAXRS_DEFAULT_SSE_BUILDER = "org.glassfish.jersey.media.sse.SseEventSource$Builder"; /** * Allows custom implementations to extend the SSE event source builder class. */ protected Builder() { } /** * Create a new SSE event source instance using the default implementation class provided by the JAX-RS * implementation provider. * * @return new SSE event source builder instance. */ static Builder newBuilder() { try { Object delegate = FactoryFinder.find(JAXRS_DEFAULT_SSE_BUILDER_PROPERTY, JAXRS_DEFAULT_SSE_BUILDER); if (!(delegate instanceof Builder)) { Class pClass = Builder.class; String classnameAsResource = pClass.getName().replace('.', '/') + ".class"; ClassLoader loader = pClass.getClassLoader(); if (loader == null) { loader = ClassLoader.getSystemClassLoader(); } URL targetTypeURL = loader.getResource(classnameAsResource); throw new LinkageError("ClassCastException: attempting to cast" + delegate.getClass().getClassLoader().getResource(classnameAsResource) + " to " + targetTypeURL); } return (Builder) delegate; } catch (Exception ex) { throw new RuntimeException(ex); } } protected abstract Builder target(WebTarget endpoint); /** * Set a custom name for the event source. *

* At present, custom event source name is mainly useful to be able to distinguish different event source * event processing threads from one another. If not set, a default name will be generated using the * SSE endpoint URI. * * @param name custom event source name. * @return updated event source builder instance. */ public abstract Builder named(String name); /** * Set the initial reconnect delay to be used by the event source. *

* Note that this value may be later overridden by the SSE endpoint using either a {@code retry} SSE event field * or HTTP 503 + {@value javax.ws.rs.core.HttpHeaders#RETRY_AFTER} mechanism as described * in the {@link SseEventSource} javadoc. * * @param delay the default time to wait before attempting to recover from a connection loss. * @param unit time unit of the reconnect delay parameter. * @return updated event source builder instance. */ public abstract Builder reconnectingEvery(long delay, TimeUnit unit); /** * Build new SSE event source pointing at a SSE streaming {@link WebTarget web target}. *

* The returned event source is ready, but not {@link SseEventSource#open() connected} to the SSE endpoint. * It is expected that you will manually invoke its {@link #open()} method once you are ready to start * receiving SSE events. In case you want to build an event source instance that is already connected * to the SSE endpoint, use the event source builder {@link #open()} method instead. *

* Once the event source is open, the incoming events are processed by the event source in an * asynchronous task that runs in an internal single-threaded {@link ScheduledExecutorService * scheduled executor service}. * * @return new event source instance, ready to be connected to the SSE endpoint. * @see #open() */ public abstract SseEventSource build(); } /** * Subscribe a {@link InboundSseEvent} consumer. *

* Given consumer is invoked once per each received event. * * @param onEvent event consumer. * @throws IllegalArgumentException when the provided parameter is {@code null}. */ void subscribe(Consumer onEvent); /** * Subscribe {@link InboundSseEvent} and {@link Throwable} consumers. *

* Event consumer is invoked once per each received event, {@code Throwable} consumer is invoked invoked upon a * unrecoverable error encountered by a {@link SseEventSource}. * * @param onEvent event consumer. * @param onError error consumer. * @throws IllegalArgumentException when the any of the provided parameters is {@code null}. */ void subscribe(Consumer onEvent, Consumer onError); /** * Subscribe {@link InboundSseEvent} and {@link Throwable} consumers and onComplete callback. *

* Event consumer is invoked once per each received event, {@code Throwable} consumer is invoked invoked upon a * unrecoverable error encountered by a {@link SseEventSource}, onComplete callback is invoked when there are no * further events to be received. * * @param onEvent event consumer. * @param onError error consumer. * @param onComplete onComplete handler. * @throws IllegalArgumentException when the any of the provided parameters is {@code null}. */ void subscribe(Consumer onEvent, Consumer onError, Runnable onComplete); /** * Subscribe {@link InboundSseEvent}, {@link Throwable} and {@code Subscription} consumers and onComplete callback. *

* Event consumer is invoked once per each received event, {@code Throwable} consumer is invoked invoked upon a * unrecoverable error encountered by a {@link SseEventSource}, onComplete callback is invoked when there are no * further events to be received. *

* Please note that calling {@link SseSubscription#request(long)} will * influence the invocation of provided event consumer and {@link SseSubscription#cancel()} will influence the invocation * of provided onComplete callback. *

* This set of parameters is meant as a replacement of having {@code Flow.Subscriber} interface copied in * javax.ws.rs package, but it behaves exactly the same. * * @param onSubscribe {@link SseSubscription} consumer. * @param onEvent event consumer. * @param onError error consumer. * @param onComplete onComplete handler. * @throws IllegalArgumentException when the any of the provided parameters is {@code null}. * @see Flow.Subscription#request(long). */ void subscribe(Consumer onSubscribe, Consumer onEvent, Consumer onError, Runnable onComplete); /** * Create a new {@link SseEventSource.Builder event source builder} that provides convenient way how to * configure and fine-tune various aspects of a newly prepared event source instance. * * @param endpoint SSE streaming endpoint. Must not be {@code null}. * @return a builder of a new event source instance pointing at the specified SSE streaming endpoint. * @throws NullPointerException in case the supplied web target is {@code null}. */ static Builder target(WebTarget endpoint) { return Builder.newBuilder().target(endpoint); } /** * Open the connection to the supplied SSE underlying {@link WebTarget web target} and start processing incoming * {@link InboundSseEvent events}. * * @throws IllegalStateException in case the event source has already been opened earlier. */ void open(); /** * Check if this event source instance has already been {@link #open() opened}. * * @return {@code true} if this event source is open, {@code false} otherwise. */ boolean isOpen(); /** * Close this event source. *

* The method will wait up to 5 seconds for the internal event processing tasks to complete. */ default void close() { close(5, TimeUnit.SECONDS); } /** * Close this event source and wait for the internal event processing task to complete * for up to the specified amount of wait time. *

* The method blocks until the event processing task has completed execution after a shutdown * request, or until the timeout occurs, or the current thread is interrupted, whichever happens * first. *

* In case the waiting for the event processing task has been interrupted, this method restores * the {@link Thread#interrupted() interrupt} flag on the thread before returning {@code false}. * * @param timeout the maximum time to wait. * @param unit the time unit of the timeout argument. * @return {@code true} if this executor terminated and {@code false} if the timeout elapsed * before termination or the termination was interrupted. */ boolean close(final long timeout, final TimeUnit unit); }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy