blob: 028f0a39296c55c34f695c32cd82021e1cb8d821 [file] [log] [blame]
/*
* Copyright (c) OSGi Alliance (2015, 2016). 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.*;
/**
* 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
*/
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 Exception 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 e The error.
* @return A new error event with the specified error.
*/
public static <T> PushEvent<T> error(Exception e) {
return new ErrorEvent<T>(e);
}
/**
* 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 Exception error;
ErrorEvent(Exception error) {
this.error = error;
}
@Override
public Exception getFailure() {
return error;
}
@Override
public EventType getType() {
return ERROR;
}
}
static final class CloseEvent<T> extends PushEvent<T> {
@Override
public EventType getType() {
return CLOSE;
}
}
}