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

org.dsa.iot.dslink.connection.ConnectionManager Maven / Gradle / Ivy

There is a newer version: 0.24.2
Show newest version
package org.dsa.iot.dslink.connection;

import org.dsa.iot.dslink.config.Configuration;
import org.dsa.iot.dslink.connection.connector.WebSocketConnector;
import org.dsa.iot.dslink.handshake.LocalHandshake;
import org.dsa.iot.dslink.handshake.RemoteHandshake;
import org.dsa.iot.dslink.provider.LoopProvider;
import org.dsa.iot.dslink.util.Objects;
import org.dsa.iot.dslink.util.URLInfo;
import org.dsa.iot.dslink.util.handler.Handler;
import org.dsa.iot.dslink.util.json.JsonObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 * @author Samuel Grenier
 */
public class ConnectionManager {

    private static final Logger LOGGER = LoggerFactory.getLogger(ConnectionManager.class);

    private final Configuration configuration;
    private final LocalHandshake localHandshake;

    private Handler preInitHandler;
    private DataHandler handler;
    private NetworkClient client;
    private int delay = 1;

    private ScheduledFuture future;
    private boolean running;

    public ConnectionManager(Configuration configuration,
                             LocalHandshake localHandshake) {
        this.configuration = configuration;
        this.localHandshake = localHandshake;
    }

    /**
     * The pre initialization handler allows the dslink to be configured
     * before the link is actually connected to the server.
     *
     * @param onClientInit Client initialization handler
     */
    public void setPreInitHandler(Handler onClientInit) {
        this.preInitHandler = onClientInit;
    }

    public synchronized void start(final Handler onClientConnected) {
        stop();
        running = true;

        LoopProvider.getProvider().schedule(new Runnable() {
            @Override
            public void run() {
                synchronized (ConnectionManager.this) {
                    if (!running) {
                        return;
                    }
                }
                LOGGER.debug("Initiating connection sequence");
                RemoteHandshake remoteHandshake = generateHandshake(new Handler() {
                    @Override
                    public void handle(Exception event) {
                        LOGGER.error("Failed to complete handshake: {}", event.getMessage());
                        reconnect();
                    }
                });

                if (remoteHandshake == null) {
                    return;
                }

                if (handler == null) {
                    handler = new DataHandler();
                }

                boolean req = localHandshake.isRequester();
                boolean resp = localHandshake.isResponder();
                String path = remoteHandshake.getPath();
                final Client cc = new Client(req, resp, path);
                cc.setHandler(handler);

                if (preInitHandler != null) {
                    preInitHandler.handle(cc);
                }

                ConnectionType type = configuration.getConnectionType();
                switch (type) {
                    case WEB_SOCKET:
                        WebSocketConnector connector = new WebSocketConnector();
                        connector.setEndpoint(configuration.getAuthEndpoint());
                        connector.setRemoteHandshake(remoteHandshake);
                        connector.setLocalHandshake(localHandshake);
                        connector.setOnConnected(new Handler() {
                            @Override
                            public void handle(Void event) {
                                if (onClientConnected != null) {
                                    onClientConnected.handle(cc);
                                }
                                cc.connected();
                            }
                        });

                        connector.setOnDisconnected(new Handler() {
                            @Override
                            public void handle(Void event) {
                                cc.disconnected();
                                if (running) {
                                    LOGGER.warn("WebSocket connection failed");
                                    reconnect();
                                }
                            }
                        });

                        connector.setOnData(new Handler() {
                            @Override
                            public void handle(JsonObject event) {
                                handler.processData(event);
                            }
                        });

                        client = connector;
                        handler.setClient(connector, remoteHandshake.getFormat());
                        connector.start();
                        break;
                    default:
                        throw new RuntimeException("Unhandled type: " + type);
                }
            }
        });
    }

    public synchronized void stop() {
        running = false;

        if (future != null) {
            future.cancel(false);
            future = null;
        }

        if (client != null) {
            client.close();
            client = null;
        }
    }

    private RemoteHandshake generateHandshake(Handler errorHandler) {
        try {
            synchronized (this) {
                if (!running) {
                    return null;
                }
            }
            URLInfo auth = configuration.getAuthEndpoint();
            return RemoteHandshake.generate(localHandshake, auth);
        } catch (Exception e) {
            if (errorHandler != null) {
                errorHandler.handle(e);
            }
        }
        return null;
    }

    private synchronized void reconnect() {
        if (!running) {
            return;
        }

        LOGGER.info("Reconnecting in {} seconds", delay);
        future = Objects.getDaemonThreadPool().schedule(new Runnable() {
            @Override
            public void run() {
                start(new Handler() {
                    @Override
                    public void handle(Client event) {
                        LOGGER.info("Connection established");
                        delay = 1;
                    }
                });
                delay *= 2;
                int cap = 60;
                if (delay > cap) {
                    delay = cap;
                }

                future = null;
            }
        }, delay, TimeUnit.SECONDS);
    }

    public static class Client {

        private final boolean isRequester;
        private final boolean isResponder;
        private final String path;

        private Handler onRequesterConnected;
        private Handler onResponderConnected;
        private Handler onRequesterDisconnected;
        private Handler onResponderDisconnected;
        private DataHandler handler;

        public Client(boolean isRequester,
                      boolean isResponder,
                      String path) {
            this.isRequester = isRequester;
            this.isResponder = isResponder;
            this.path = path;
        }

        public DataHandler getHandler() {
            return handler;
        }

        void setHandler(DataHandler handler) {
            this.handler = handler;
        }

        public boolean isRequester() {
            return isRequester;
        }

        public boolean isResponder() {
            return isResponder;
        }

        public String getPath() {
            return path;
        }

        public void setRequesterOnConnected(Handler handler) {
            this.onRequesterConnected = handler;
        }

        public void setResponderOnConnected(Handler handler) {
            this.onResponderConnected = handler;
        }

        public void setRequesterOnDisconnected(Handler handler) {
            this.onRequesterDisconnected = handler;
        }

        public void setResponderOnDisconnected(Handler handler) {
            this.onResponderDisconnected = handler;
        }

        void connected() {
            if (onRequesterConnected != null) {
                onRequesterConnected.handle(this);
            }

            if (onResponderConnected != null) {
                onResponderConnected.handle(this);
            }
        }

        void disconnected() {
            if (onRequesterDisconnected != null) {
                onRequesterDisconnected.handle(null);
            }

            if (onResponderDisconnected != null) {
                onResponderDisconnected.handle(null);
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy