com.arm.mbed.cloud.sdk.subscribe.AbstractObserver Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mbed-cloud-sdk Show documentation
Show all versions of mbed-cloud-sdk Show documentation
The Pelion Cloud SDK (formerly known as Mbed Cloud SDK) provides a simplified interface to the Pelion Cloud APIs by exposing functionality using conventions and paradigms familiar to Java developers.
package com.arm.mbed.cloud.sdk.subscribe;
import java.util.concurrent.Future;
import org.reactivestreams.Subscription;
import io.reactivex.Flowable;
import io.reactivex.Single;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.functions.Action;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Predicate;
import com.arm.mbed.cloud.sdk.annotations.Internal;
import com.arm.mbed.cloud.sdk.annotations.NonNull;
import com.arm.mbed.cloud.sdk.annotations.Nullable;
import com.arm.mbed.cloud.sdk.annotations.Preamble;
import com.arm.mbed.cloud.sdk.common.CallbackWithException;
import com.arm.mbed.cloud.sdk.common.MbedCloudException;
import com.arm.mbed.cloud.sdk.common.TimePeriod;
import com.arm.mbed.cloud.sdk.common.listing.FilterOptions;
@Preamble(description = "Abstract subscription observer")
@Internal
public abstract class AbstractObserver implements Observer {
private final Flowable flow;
protected final SubscriptionManager manager;
protected final FilterOptions filter;
private final CompositeDisposable composite;
private final String id;
private final CallbackWithException actionOnSubscription;
private final CallbackWithException actionOnUnsubscription;
private volatile boolean isDisposed;
private volatile boolean hasExecutedActionOnFirstSubscription;
/**
* Constructor.
*
* @param manager
* subscription manager.
* @param id
* observer id.
* @param flow
* communication channel. @see
* RxJava Flowable
* @param filter
* filter to apply
* @param unsubscribeOnCompletion
* states whether the manager should unsubscribe the observer when the communication channel is closed.
* @param actionOnSubscription
* action to perform on subscription
* @param actionOnUnsubscription
* action to perform on unsubscription
*/
@SuppressWarnings("unchecked")
public AbstractObserver(SubscriptionManager manager, String id, Flowable flow, FilterOptions filter,
boolean unsubscribeOnCompletion,
CallbackWithException extends FilterOptions, MbedCloudException> actionOnSubscription,
CallbackWithException extends FilterOptions, MbedCloudException> actionOnUnsubscription) {
super();
isDisposed = false;
hasExecutedActionOnFirstSubscription = false;
this.filter = filter;
this.composite = new CompositeDisposable();
this.id = id;
final boolean mustUnsubscribeOnCompletion = unsubscribeOnCompletion;
this.actionOnSubscription = (CallbackWithException) actionOnSubscription;
this.actionOnUnsubscription = (CallbackWithException) actionOnUnsubscription;
this.flow = flow.observeOn(manager.getObservedOnExecutor()).filter(new Predicate() {
@Override
public boolean test(T value) throws Exception {
return verifiesFilter(value);
}
}).doOnSubscribe(createOnSubscriptionAction()).doFinally(createTerminationAction(mustUnsubscribeOnCompletion));
this.manager = manager;
}
private Consumer super Subscription> createOnSubscriptionAction() {
return new Consumer() {
@Override
public void accept(Subscription subscription) throws Exception {
if (actionOnSubscription != null && !hasExecutedActionOnFirstSubscription) {
actionOnSubscription.execute(filter);
hasExecutedActionOnFirstSubscription = true;
}
}
};
}
private Action createTerminationAction(final boolean mustUnsubscribeOnCompletion) {
return new Action() {
@Override
public void run() throws Exception {
if (mustUnsubscribeOnCompletion) {
unsubscribe();
}
}
};
}
@Override
public void notify(@NonNull T data) throws MbedCloudException {
final NotificationMessage message = new NotificationMessage<>(data, null);
notify(message);
}
@Override
public void notify(@NonNull NotificationMessage message) throws MbedCloudException {
if (isDisposed) {
throw new MbedCloudException("The subscription channel has been disposed");
}
if (message != null) {
manager.notify(getSubscriptionType(), getId(), message);
}
}
@Override
public void addCallback(NotificationCallback callback) {
if (callback != null) {
final NotificationCallback immutableCallback = callback;
composite.add(flow.subscribe(new Consumer() {
@Override
public void accept(T value) throws Exception {
immutableCallback.callBack(value);
}
}, new Consumer() {
@Override
public void accept(Throwable throwable) throws Exception {
immutableCallback.callBack(throwable);
}
}));
}
}
@Override
public void removeCallback() {
composite.clear();
}
@Override
public void unsubscribe() throws MbedCloudException {
if (actionOnUnsubscription != null) {
actionOnUnsubscription.execute(this.filter);
}
removeCallback();
manager.unsubscribe(this);
isDisposed = true;
}
@Override
public Flowable flow() {
return flow;
}
/*
* (non-Javadoc)
*
* @see com.arm.mbed.cloud.sdk.subscribe.Observer#futureOne()
*/
@Override
public Future futureOne() throws MbedCloudException {
return futureOne(null);
}
/*
* (non-Javadoc)
*
* @see com.arm.mbed.cloud.sdk.subscribe.Observer#futureOne(com.arm.mbed.cloud.sdk.common.TimePeriod)
*/
@Override
public Future futureOne(@Nullable TimePeriod timeout) throws MbedCloudException {
final Flowable flowWithTimeout = specifyTimeout(timeout);
return flowWithTimeout.take(1).toFuture();
}
@Override
public T one() throws MbedCloudException {
return one(null);
}
/*
* (non-Javadoc)
*
* @see com.arm.mbed.cloud.sdk.subscribe.Observer#one(com.arm.mbed.cloud.sdk.common.TimePeriod)
*/
@Override
public T one(@Nullable TimePeriod timeout) throws MbedCloudException {
try {
final T value = singleNotification(timeout).blockingGet();
unsubscribe();
return value;
} catch (Exception exception) {
throw new MbedCloudException("The value could not be retrieved", exception);
}
}
@Override
public Single singleNotification(TimePeriod timeout) throws MbedCloudException {
try {
final Flowable oneItemFlow = specifyTimeout(timeout);
return oneItemFlow.firstOrError();
} catch (Exception exception) {
throw new MbedCloudException("The value could not be retrieved", exception);
}
}
protected Flowable specifyTimeout(TimePeriod timeout) {
return (timeout == null) ? flow : flow.timeout(timeout.getDuration(), timeout.getUnit());
}
@Override
public boolean isDisposed() {
return isDisposed;
}
@Override
public String getId() {
return id;
}
@Override
public int numberOfCallbacks() {
return composite.size();
}
/**
* Gets the filter applied to this channel.
*
* @return the filter
*/
public FilterOptions getFilter() {
return filter;
}
/**
* Gets the callbacks applied to this channel.
*
* @return callbacks
*/
public CompositeDisposable getCallbacks() {
return composite;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Observer [id=" + id + ", type=" + getSubscriptionType() + ", filter=" + filter + ", isDisposed="
+ isDisposed + "]";
}
/**
* Tests whether the value complies with the filter or not.
*
* @param value
* notification values
* @return true if the notification value verifies the filter. False otherwise.
*/
protected abstract boolean verifiesFilter(T value);
@Override
public abstract SubscriptionType getSubscriptionType();
}