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

io.relayr.java.websocket.WebSocketClient Maven / Gradle / Ivy

package io.relayr.java.websocket;

import com.google.gson.Gson;

import org.eclipse.paho.client.mqttv3.MqttException;

import java.util.HashMap;
import java.util.Map;

import javax.inject.Inject;
import javax.inject.Singleton;

import io.relayr.java.api.ChannelApi;
import io.relayr.java.model.DataPackage;
import io.relayr.java.model.Device;
import io.relayr.java.model.action.Reading;
import io.relayr.java.model.channel.ChannelDefinition;
import io.relayr.java.model.channel.DataChannel;
import io.relayr.java.model.channel.PublishChannel;
import rx.Observable;
import rx.Observer;
import rx.Subscriber;
import rx.functions.Action1;
import rx.functions.Func1;
import rx.schedulers.Schedulers;
import rx.subjects.PublishSubject;

@Singleton
public class WebSocketClient {

    final ChannelApi mChannelApi;
    final WebSocket mWebSocket;
    final Map mDeviceChannels = new HashMap<>();
    final Map mPublishChannels = new HashMap<>();
    final Map> mSocketConnections = new HashMap<>();

    @Inject
    public WebSocketClient(ChannelApi channelApi, WebSocketFactory factory) {
        mChannelApi = channelApi;
        mWebSocket = factory.createWebSocket();
    }

    public Observable subscribe(Device device) {
        return subscribe(device.getId());
    }

    public Observable subscribe(String deviceId) {
        if (mSocketConnections.containsKey(deviceId))
            return mSocketConnections.get(deviceId);
        else
            return start(deviceId);
    }

    public void unSubscribe(final String deviceId) {
        if (mSocketConnections.containsKey(deviceId)) {
            mSocketConnections.get(deviceId).onCompleted();
            mSocketConnections.remove(deviceId);
        }

        if (!mDeviceChannels.isEmpty() && mDeviceChannels.containsKey(deviceId))
            if (mWebSocket.unSubscribe(mDeviceChannels.get(deviceId).getCredentials().getTopic()))
                mDeviceChannels.remove(deviceId);
    }

    public Observable publish(final String deviceId, final Reading payload) {
        Observable observable = Observable.create(new Observable.OnSubscribe() {
            @Override public void call(final Subscriber subscriber) {
                if (mPublishChannels.containsKey(deviceId))
                    publish(deviceId, payload, subscriber);
                else
                    mChannelApi.createForDevice(new ChannelDefinition(deviceId, "mqtt"), deviceId)
                            .flatMap(new Func1>() {
                                @Override
                                public Observable call(PublishChannel channel) {
                                    return mWebSocket.createClient(channel);
                                }
                            })
                            .subscribe(new Observer() {
                                @Override public void onCompleted() {}

                                @Override public void onError(Throwable e) {
                                    mPublishChannels.remove(deviceId);
                                    subscriber.onError(e);
                                }

                                @Override public void onNext(DataChannel channel) {
                                    if (!mPublishChannels.containsKey(deviceId))
                                        mPublishChannels.put(deviceId, channel);
                                    publish(deviceId, payload, subscriber);
                                }
                            });
            }
        });

        observable.subscribe(new Observer() {
            @Override public void onCompleted() {}

            @Override public void onError(Throwable e) {
            }

            @Override public void onNext(Void aVoid) {}
        });
        return observable;
    }

    private void publish(String deviceId, Reading payload, Subscriber subscriber) {
        try {
            mWebSocket.publish(mPublishChannels.get(deviceId).getCredentials().getTopic() + "/data",
                    new Gson().toJson(payload));
            subscriber.onNext(null);
        } catch (MqttException e) {
            subscriber.onError(e);
        }
    }

    private synchronized Observable start(final String deviceId) {
        final PublishSubject subject = PublishSubject.create();
        mSocketConnections.put(deviceId, subject);

        mChannelApi.create(new ChannelDefinition(deviceId, "mqtt"))
                .flatMap(new Func1>() {
                    @Override
                    public Observable call(final DataChannel channel) {
                        return mWebSocket.createClient(channel);
                    }
                })
                .subscribeOn(Schedulers.newThread())
                .subscribe(new Subscriber() {
                    @Override
                    public void onCompleted() {
                    }

                    @Override
                    public void onError(Throwable e) {
                        e.printStackTrace();
                        mSocketConnections.remove(deviceId);
                    }

                    @Override
                    public void onNext(DataChannel channel) {
                        subscribeToChannel(channel, deviceId, subject);
                    }
                });

        return subject.doOnError(new Action1() {
            @Override
            public void call(Throwable throwable) {
                unSubscribe(deviceId);
            }
        });
    }

    private void subscribeToChannel(final DataChannel channel, final String deviceId,
                                    final PublishSubject subject) {
        mWebSocket.subscribe(channel.getCredentials().getTopic(), channel.getChannelId(), new WebSocketCallback() {
            @Override
            public void connectCallback(Object message) {
                if (!mDeviceChannels.containsKey(deviceId))
                    mDeviceChannels.put(deviceId, channel);
            }

            @Override
            public void disconnectCallback(Object message) {
                subject.onError((Throwable) message);
                mDeviceChannels.remove(deviceId);
                mSocketConnections.remove(deviceId);
            }

            @Override
            public void successCallback(Object message) {
                DataPackage dataPackage = new Gson().fromJson(message.toString(), DataPackage.class);
                for (DataPackage.Data dataPoint : dataPackage.readings) {
                    subject.onNext(new Reading(dataPackage.received, dataPoint.recorded,
                            dataPoint.meaning, dataPoint.path, dataPoint.value));
                }
            }

            @Override
            public void errorCallback(Throwable e) {
                subject.onError(e);
                mDeviceChannels.remove(deviceId);
                mSocketConnections.remove(deviceId);
            }
        });
    }
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy