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

com.github.automately.java.MessageBus Maven / Gradle / Ivy

The newest version!
package com.github.automately.java;

import com.github.automately.java.data.EventBus;
import io.jsync.Async;
import io.jsync.AsyncFactory;
import io.jsync.Handler;
import io.jsync.VoidHandler;
import io.jsync.json.JsonObject;
import io.jsync.logging.Logger;
import io.jsync.logging.impl.LoggerFactory;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.concurrent.TimeUnit;

/**
 * The MessageBus is a pretty powerful feature that Automately offers.
 * It is basically a clustered messaging system that allows your code to
 * communicate at scale.
 */
public class MessageBus {

    private static long DEFAULT_TIMEOUT = TimeUnit.SECONDS.toMillis(25);

    private final Async async = AsyncFactory.newAsync();

    private final Logger logger = LoggerFactory.getLogger(MessageBus.class);

    private boolean stopping = false;
    private boolean initialized = false;
    private EventBus eventBus = null;

    public boolean stopping() {
        return stopping;
    }

    public boolean initialized() {
        return initialized && !stopping;
    }

    public MessageBus initialize(String publicKey, final Handler callback) {
        return initialize(publicKey, null, callback);
    }

    public MessageBus initialize(String publicKey, String key, final Handler callback) {

        if (publicKey == null || publicKey.isEmpty()) {
            throw new RuntimeException("Your public api key cannot be empty.");
        }

        eventBus = new EventBus(async);

        eventBus.onOpen(event -> authorize(event1 -> {
            if (!event1) {
                // We want to wait at least 30 seconds in case the connection is slow
                final long loginFailTimer = async.setTimer(DEFAULT_TIMEOUT, event2 -> callback.handle(false));
                Handler authHandler = authEvent -> {
                    async.cancelTimer(loginFailTimer);
                    JsonObject body = new JsonObject(authEvent.toString());
                    if (!body.containsField("status") || body.getString("status").equals("ok")) {
                        authorize(event3 -> {
                            if (event3) {
                                if (callback != null) {
                                    initialized = true;
                                    callback.handle(true);
                                }
                            } else {
                                if (callback != null) {
                                    callback.handle(false);
                                }
                            }
                        });
                    } else {
                        if (callback != null) {
                            callback.handle(false);
                        }
                    }
                };

                if (key != null && !key.isEmpty()) {
                    eventBus.authenticate(publicKey, key, authHandler);
                } else {
                    eventBus.login(publicKey, authHandler);
                }
            } else {
                initialized = true;
                callback.handle(true);
            }
        }));

        eventBus.onClose(new VoidHandler() {
            @Override
            protected void handle() {
                if (!stopping) {
                    // Handle close
                }
            }
        });

        eventBus.connect();

        return this;

    }

    private void authorize(final Handler callback) {
        if (eventBus == null) {
            if (callback != null) {
                callback.handle(false);
            }
            return;
        }

        final Runnable authorizeNow = new Runnable() {

            private long authRetryTimer = 0;

            private Runnable self = this;

            @Override
            public void run() {
                if (authRetryTimer == 0) {
                    authRetryTimer = async.setTimer(TimeUnit.SECONDS.toMillis(5), event -> self.run());
                }
                eventBus.authorise(event1 -> {
                    async.cancelTimer(authRetryTimer);
                    JsonObject body = new JsonObject(event1.toString());
                    if (body.containsField("status") && body.getString("status").equals("ok")) {
                        if (callback != null) {
                            callback.handle(true);
                        }
                    } else {
                        if (callback != null) {
                            callback.handle(false);
                        }
                    }
                });
            }
        };
        authorizeNow.run();
    }

    private HashMap>> registeredHandlers = new LinkedHashMap<>();

    public MessageBus registerHandler(String identifier, Handler handler) {

        if (!initialized || eventBus.readyState() != EventBus.OPEN) {
            logger.error("Failed to register handler because the EventBus is not ready");
            return this;
        }

        JsonObject data = new JsonObject()
                .putString("sessionID", eventBus.sessionID())
                .putString("type", "register")
                .putString("identifier", identifier);

        eventBus.send("automately.sdk.messageBus", data, event -> {
            JsonObject response = new JsonObject(event.toString());
            if (response.containsField("status") && response.getString("status").equals("ok")) {
                String newId = response.getString("eb_identifier");
                HashMap> handlers = registeredHandlers.get(identifier);
                if (handlers == null) {
                    handlers = new LinkedHashMap<>();
                }
                handlers.put(newId, handler);
                registeredHandlers.put(identifier, handlers);
                eventBus.registerHandler(newId, handler);
            } else {
                logger.error("Something went wrong while attempting to register a handler for the identifier " + identifier);
            }
        });

        return this;
    }

    public MessageBus unregisterHandler(String identifier, Handler handler) {
        if (registeredHandlers.containsKey(identifier)) {
            HashMap> handlers = registeredHandlers.get(identifier);
            for (String key : handlers.keySet()) {
                if (handlers.get(key).equals(handler)) {
                    eventBus.unregisterHandler(key, handler);
                    handlers.remove(key, handler);
                }
            }
        }
        return this;
    }

    public MessageBus publish(String identifier, Object message) {
        String type = "publish";

        JsonObject data = new JsonObject()
                .putString("sessionID", eventBus.sessionID())
                .putString("type", type)
                .putString("identifier", identifier)
                .putValue("message", message);

        // We don't want this to hit multiple sdk servers
        // so we do a "send" since the sdk server will publish
        // the message
        eventBus.send("automately.sdk.messageBus", data);

        return this;
    }

    public MessageBus send(String identifier, Object message) {
        return send(identifier, message, null);
    }

    public MessageBus send(String identifier, Object message, Handler handler) {

        String type = "send";

        if (handler != null) {
            type = "sendWithReply";
        }

        JsonObject data = new JsonObject()
                .putString("sessionID", eventBus.sessionID())
                .putString("type", type)
                .putString("identifier", identifier)
                .putValue("message", message);

        eventBus.send("automately.sdk.messageBus", data, event -> {
            if (handler != null) {
                handler.handle(event);
            }
        });

        return this;
    }

    public MessageBus stop() {
        if (initialized && eventBus != null) {
            try {
                stopping = true;
                eventBus.close();
            } finally {
                initialized = false;
                eventBus = null;
                stopping = false;
            }
        }
        return this;
    }
}