| /* |
| * Copyright (c) OSGi Alliance (2015, 2017). All Rights Reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.osgi.util.pushstream; |
| |
| import static org.osgi.util.pushstream.PushEvent.EventType.*; |
| |
| import org.osgi.annotation.versioning.ProviderType; |
| |
| /** |
| * A PushEvent is an immutable object that is transferred through a |
| * communication channel to push information to a downstream consumer. The event |
| * has three different types: |
| * <ul> |
| * <li>{@link EventType#DATA} – Provides access to a typed data element in the |
| * stream. |
| * <li>{@link EventType#CLOSE} – The stream is closed. After receiving this |
| * event, no more events will follow. |
| * <li>{@link EventType#ERROR} – The stream ran into an unrecoverable problem |
| * and is sending the reason downstream. The stream is closed and no more events |
| * will follow after this event. |
| * </ul> |
| * |
| * @param <T> The payload type of the event. |
| * @Immutable |
| */ |
| @ProviderType |
| public abstract class PushEvent<T> { |
| |
| /** |
| * The type of a {@link PushEvent}. |
| */ |
| public static enum EventType { |
| /** |
| * A data event forming part of the stream |
| */ |
| DATA, |
| /** |
| * An error event that indicates streaming has failed and that no more |
| * events will arrive |
| */ |
| ERROR, |
| /** |
| * An event that indicates that the stream has terminated normally |
| */ |
| CLOSE |
| } |
| |
| /** |
| * Package private default constructor. |
| */ |
| PushEvent() {} |
| |
| /** |
| * Get the type of this event. |
| * |
| * @return The type of this event. |
| */ |
| public abstract EventType getType(); |
| |
| /** |
| * Return the data for this event. |
| * |
| * @return The data payload. |
| * @throws IllegalStateException if this event is not a |
| * {@link EventType#DATA} event. |
| */ |
| public T getData() throws IllegalStateException { |
| throw new IllegalStateException( |
| "Not a DATA event, the event type is " + getType()); |
| } |
| |
| /** |
| * Return the error that terminated the stream. |
| * |
| * @return The error that terminated the stream. |
| * @throws IllegalStateException if this event is not an |
| * {@link EventType#ERROR} event. |
| */ |
| public Throwable getFailure() throws IllegalStateException { |
| throw new IllegalStateException( |
| "Not an ERROR event, the event type is " + getType()); |
| } |
| |
| /** |
| * Answer if no more events will follow after this event. |
| * |
| * @return {@code false} if this is a data event, otherwise {@code true}. |
| */ |
| public boolean isTerminal() { |
| return true; |
| } |
| |
| /** |
| * Create a new data event. |
| * |
| * @param <T> The payload type. |
| * @param payload The payload. |
| * @return A new data event wrapping the specified payload. |
| */ |
| public static <T> PushEvent<T> data(T payload) { |
| return new DataEvent<T>(payload); |
| } |
| |
| /** |
| * Create a new error event. |
| * |
| * @param <T> The payload type. |
| * @param t The error. |
| * @return A new error event with the specified error. |
| */ |
| public static <T> PushEvent<T> error(Throwable t) { |
| return new ErrorEvent<T>(t); |
| } |
| |
| /** |
| * Create a new close event. |
| * |
| * @param <T> The payload type. |
| * @return A new close event. |
| */ |
| public static <T> PushEvent<T> close() { |
| return new CloseEvent<T>(); |
| } |
| |
| /** |
| * Convenience to cast a close/error event to another payload type. Since |
| * the payload type is not needed for these events this is harmless. This |
| * therefore allows you to forward the close/error event downstream without |
| * creating anew event. |
| * |
| * @param <X> The new payload type. |
| * @return The current error or close event mapped to a new payload type. |
| * @throws IllegalStateException if the event is a {@link EventType#DATA} |
| * event. |
| */ |
| public <X> PushEvent<X> nodata() throws IllegalStateException { |
| @SuppressWarnings("unchecked") |
| PushEvent<X> result = (PushEvent<X>) this; |
| return result; |
| } |
| |
| static final class DataEvent<T> extends PushEvent<T> { |
| private final T data; |
| |
| DataEvent(T data) { |
| this.data = data; |
| } |
| |
| @Override |
| public T getData() throws IllegalStateException { |
| return data; |
| } |
| |
| @Override |
| public EventType getType() { |
| return DATA; |
| } |
| |
| @Override |
| public boolean isTerminal() { |
| return false; |
| } |
| |
| @Override |
| public <X> PushEvent<X> nodata() throws IllegalStateException { |
| throw new IllegalStateException("This event is a DATA event"); |
| } |
| } |
| |
| static final class ErrorEvent<T> extends PushEvent<T> { |
| private final Throwable error; |
| |
| ErrorEvent(Throwable error) { |
| this.error = error; |
| } |
| |
| @Override |
| public Throwable getFailure() { |
| return error; |
| } |
| |
| @Override |
| public EventType getType() { |
| return ERROR; |
| } |
| } |
| |
| static final class CloseEvent<T> extends PushEvent<T> { |
| @Override |
| public EventType getType() { |
| return CLOSE; |
| } |
| } |
| } |