com.mi.xserv.Xserv.java Source code

Java tutorial

Introduction

Here is the source code for com.mi.xserv.Xserv.java

Source

/***
 * Xserv
 * <p>
 * Copyright (C) 2015 Giovanni Amati
 * <p>
 * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.
 ***/

package com.mi.xserv;

import android.net.Uri;
import android.os.Build;
import android.util.Base64;

import com.koushikdutta.async.callback.CompletedCallback;
import com.koushikdutta.async.http.AsyncHttpClient;
import com.koushikdutta.async.http.AsyncHttpRequest;
import com.koushikdutta.async.http.AsyncHttpResponse;
import com.koushikdutta.async.http.WebSocket;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;

import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

public class Xserv extends XservBase {
    public final static int OP_PUBLISH = 200;
    public final static int OP_SUBSCRIBE = 201;
    public final static int OP_UNSUBSCRIBE = 202;
    public final static int OP_HISTORY = 203;
    public final static int OP_USERS = 204;
    public final static int OP_TOPICS = 205;
    public final static int OP_UPDATE = 300;
    public final static int OP_UPDATE_ALL = 301;
    public final static int OP_DELETE = 302;
    public final static int OP_DELETE_ALL = 303;
    public final static int OP_JOIN = 401;
    public final static int OP_LEAVE = 402;
    // op result_code
    public final static int RC_OK = 1;
    public final static int RC_GENERIC_ERROR = 0;
    public final static int RC_ARGS_ERROR = -1;
    public final static int RC_ALREADY_SUBSCRIBED = -2;
    public final static int RC_UNAUTHORIZED = -3;
    public final static int RC_NO_TOPIC = -4;
    public final static int RC_NO_DATA = -5;
    public final static int RC_NOT_PRIVATE = -6;
    public final static int RC_LIMIT_MESSAGES = -7;
    public final static int RC_DB_ERROR = -8;
    // op
    private final static int OP_HANDSHAKE = 100;
    private final static String TAG = "Xserv";
    private final static String VERSION = "1";
    private final static String HOST = "192.168.130.187";
    // private final static String HOST = "mobile-italia.com";
    private final static String PORT = "4332";
    private final static String TLS_PORT = "8332";
    private final static String URL = "ws%1$s://%2$s:%3$s/ws/%4$s?version=%5$s";
    private final static String DEFAULT_AUTH_URL = "http%1$s://%2$s:%3$s/1/user";
    private final static int DEFAULT_RI = 5000;
    // callbacks
    private static HashMap<String, OnCompletionListener> mCallbacks;
    // attributes
    private String mAppId;
    private Future<WebSocket> mConn;
    private int mReconnectInterval;
    private boolean isAutoReconnect;
    private JSONObject mUserData;
    private boolean isConnected;
    private boolean isSecure;
    private String mDeviceToken;

    /**
     * Return an instance of Xserv connector.
     *
     * @param app_id identifier of your application. You can find it on Xserv Dashboard.
     */
    public Xserv(String app_id) {
        super();

        mAppId = app_id;
        mConn = null;
        mUserData = new JSONObject();
        mReconnectInterval = DEFAULT_RI;

        isAutoReconnect = false;
        isConnected = false;

        // TLS
        isSecure = true;

        // Callbacks
        mCallbacks = new HashMap<>();
    }

    public static boolean isPrivateTopic(String topic) {
        return topic.startsWith("@");
    }

    public void disableTLS() {
        isSecure = false;
    }

    public boolean isConnected() {
        return mConn != null && isConnected;
    }

    public void connect() {
        connect(false);
    }

    private void connect(boolean no_ar) {
        if (!no_ar) {
            isAutoReconnect = true;
        }

        if (!isConnected()) {
            String protocol = "";
            String port = PORT;
            if (isSecure) {
                protocol = "s";
                port = TLS_PORT;
            }

            mCallbacks.clear();

            AsyncHttpClient as = AsyncHttpClient.getDefaultInstance();
            mConn = as.websocket(String.format(URL, protocol, HOST, port, mAppId, VERSION), null,
                    new AsyncHttpClient.WebSocketConnectCallback() {

                        @Override
                        public void onCompleted(Exception e, WebSocket ws) {
                            if (e == null) {
                                setOtherWsCallback(ws);

                                handshake();
                            } else {
                                mCallbacks.clear();

                                onErrorConnection(e);

                                // eccezione, error socket in connection non e' la error
                                reConnect();
                            }
                        }
                    });
        }
    }

    private void setOtherWsCallback(WebSocket ws) {
        ws.setClosedCallback(new CompletedCallback() {

            @Override
            public void onCompleted(Exception e) {
                isConnected = false;

                mCallbacks.clear();

                onCloseConnection(e);

                reConnect();
            }
        });

        ws.setStringCallback(new WebSocket.StringCallback() {

            @Override
            public void onStringAvailable(String event) {
                manageMessage(event);
            }
        });
    }

    private void manageMessage(String event) {
        JSONObject json = null;
        try {
            json = new JSONObject(event);
        } catch (JSONException ignored) {
        }

        if (json != null) {
            int op = 0;
            try {
                op = json.getInt("op");
            } catch (JSONException ignored) {
            }

            if (op == 0) {
                // messages

                onReceiveMessages(json);
            } else if (op > 0) {
                // operations

                try {
                    String data = json.getString("data");
                    byte[] b = Base64.decode(data, Base64.DEFAULT);
                    json.put("data", new String(b, "UTF-8")); // string
                } catch (JSONException | UnsupportedEncodingException ignored) {
                }

                JSONObject json_data = null;
                try {
                    String data = json.getString("data");
                    Object type = new JSONTokener(data).nextValue();
                    if (type instanceof JSONObject) {
                        json_data = new JSONObject(data);

                        json.put("data", json_data);
                    } else if (type instanceof JSONArray) {
                        json.put("data", new JSONArray(data));
                    }
                } catch (JSONException ignored) {
                }

                try {
                    json.put("name", stringifyOp(op));
                } catch (JSONException ignored) {
                }

                int rc = 0;
                String uuid = "";
                String topic = "";
                String descr = "";
                try {
                    rc = json.getInt("rc");
                    uuid = json.getString("uuid");
                    topic = json.getString("topic");
                    descr = json.getString("descr");
                } catch (JSONException ignored) {
                }

                if (op == OP_HANDSHAKE) {
                    // handshake

                    if (rc == RC_OK) {
                        if (json_data != null) {
                            setUserData(json_data);

                            isConnected = true;

                            onOpenConnection();
                        } else {
                            mCallbacks.clear();

                            onErrorConnection(new Exception(descr));
                        }
                    } else {
                        mCallbacks.clear();

                        onErrorConnection(new Exception(descr));
                    }
                } else {
                    // classic operations

                    if (op == OP_SUBSCRIBE && isPrivateTopic(topic) && rc == RC_OK) {
                        if (json_data != null) {
                            setUserData(json_data);
                        }
                    }

                    if (mCallbacks.get(uuid) != null) {
                        mCallbacks.get(uuid).onCompletion(json);
                        mCallbacks.remove(uuid);
                    } else {
                        OnReceiveOperations(json);
                    }
                }
            }
        }
    }

    private void reConnect() {
        if (isAutoReconnect && mReconnectInterval > 0) {
            getMainLooper().postDelayed(new Runnable() {

                @Override
                public void run() {
                    connect(true);
                }
            }, mReconnectInterval);
        }
    }

    public void disconnect() {
        isAutoReconnect = false;

        if (isConnected()) {
            try {
                mConn.get().close();
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
    }

    public Integer getReconnectInterval() {
        return mReconnectInterval;
    }

    public void setReconnectInterval(Integer milliseconds) {
        mReconnectInterval = milliseconds;
    }

    public String getDeviceToken() {
        if (mDeviceToken == null) {
            mDeviceToken = "";
        }
        return mDeviceToken;
    }

    public void setDeviceToken(String device_token) {
        mDeviceToken = device_token;
    }

    private void handshake() {
        JSONObject stat = new JSONObject();
        try {
            String model = Build.MODEL;
            if (model.length() > 45) {
                model = model.substring(0, 45);
            }

            stat.put("uuid", getDeviceID());
            stat.put("device_token", getDeviceToken());
            stat.put("model", model);
            stat.put("os", "Android " + Build.VERSION.RELEASE);
            stat.put("tz_offset", getTimeZoneOffset());
            stat.put("tz_dst", getTimeZoneDst());
            stat.put("lang", getLanguage());
        } catch (JSONException e) {
            e.printStackTrace();
        }
        wsSend(stat);
    }

    private void send(final JSONObject json) {
        if (!isConnected())
            return;

        int op = 0;
        String topic = "";
        try {
            op = json.getInt("op");
            topic = json.getString("topic");
        } catch (JSONException ignored) {
        }

        if (op == OP_SUBSCRIBE && isPrivateTopic(topic)) {
            JSONObject auth = null;
            try {
                auth = json.getJSONObject("auth");
            } catch (JSONException ignored) {
            }

            if (auth != null) {
                String protocol = "";
                String port = PORT;
                if (isSecure) {
                    protocol = "s";
                    port = TLS_PORT;
                }

                String endpoint = String.format(DEFAULT_AUTH_URL, protocol, HOST, port);
                try {
                    endpoint = auth.getString("endpoint");
                } catch (JSONException ignored) {
                }

                JSONObject params = null;
                try {
                    params = auth.getJSONObject("params");
                } catch (JSONException ignored) {
                }

                String user = "";

                // params
                if (params != null) {
                    try {
                        user = params.getString("user");
                    } catch (JSONException ignored) {
                    }

                    try {
                        params.put("socket_id", getSocketId());
                        params.put("topic", topic);
                    } catch (JSONException ignored) {
                    }

                    endpoint += "?" + urlEncodedJSON(params);
                }

                AsyncHttpRequest request = new AsyncHttpRequest(Uri.parse(endpoint), "GET");

                // add custom headers
                try {
                    JSONObject headers = auth.getJSONObject("headers");
                    Iterator<?> keys = headers.keys();
                    while (keys.hasNext()) {
                        String key = (String) keys.next();
                        request.setHeader(key, (String) headers.get(key));
                    }
                } catch (JSONException ignored) {
                }
                request.setHeader("X-Xserv-AppId", mAppId);

                final String userStatic = user;

                AsyncHttpClient as = AsyncHttpClient.getDefaultInstance();
                as.executeJSONObject(request, new AsyncHttpClient.JSONObjectCallback() {
                    @Override
                    public void onCompleted(Exception e, AsyncHttpResponse source, JSONObject result) {
                        if (e == null) {
                            json.remove("auth");

                            try {
                                json.put("arg1", userStatic);
                                json.put("arg2", result.getString("data"));
                                json.put("arg3", result.getString("sign"));
                            } catch (JSONException ignored) {
                            }

                            wsSend(json);
                        } else {
                            json.remove("auth");

                            wsSend(json);
                        }
                    }
                });
            } else {
                wsSend(json);
            }
        } else {
            wsSend(json);
        }
    }

    private void wsSend(JSONObject json) {
        try {
            mConn.get().send(json.toString());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }

    public String getSocketId() {
        String socket_id = "";
        try {
            socket_id = mUserData.getString("socket_id");
        } catch (JSONException ignored) {
            // e.printStackTrace();
        }
        return socket_id;
    }

    public JSONObject getUserData() {
        return mUserData;
    }

    private void setUserData(JSONObject json) {
        mUserData = json;
    }

    private String stringifyOp(int code) {
        switch (code) {
        case OP_SUBSCRIBE:
            return "subscribe";
        case OP_UNSUBSCRIBE:
            return "unsubscribe";
        case OP_HISTORY:
            return "history";
        case OP_USERS:
            return "users";
        case OP_JOIN:
            return "join";
        case OP_LEAVE:
            return "leave";
        case OP_PUBLISH:
            return "publish";
        case OP_HANDSHAKE:
            return "handshake";
        case OP_TOPICS:
            return "topics";
        case OP_UPDATE:
            return "update";
        case OP_UPDATE_ALL:
            return "update_all";
        case OP_DELETE:
            return "delete";
        case OP_DELETE_ALL:
            return "delete_all";
        }
        return "";
    }

    public String publish(String topic, Object data) {
        return publish(topic, data, null);
    }

    public String publish(String topic, Object data, OnCompletionListener listener) {
        if (!isConnected())
            return "";

        String uuid = UUID.randomUUID().toString();
        if (listener != null) {
            mCallbacks.put(uuid, listener);
        }
        JSONObject json = new JSONObject();
        try {
            json.put("uuid", uuid);
            json.put("op", OP_PUBLISH);
            json.put("topic", topic);
            json.put("arg1", data);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        send(json);
        return uuid;
    }

    public String update(String topic, String object_id, Object data) {
        return update(topic, object_id, data, null);
    }

    public String update(String topic, String object_id, Object data, OnCompletionListener listener) {
        if (!isConnected())
            return "";

        String uuid = UUID.randomUUID().toString();
        if (listener != null) {
            mCallbacks.put(uuid, listener);
        }
        JSONObject json = new JSONObject();
        try {
            json.put("uuid", uuid);
            json.put("op", OP_UPDATE);
            json.put("topic", topic);
            json.put("arg1", data);
            json.put("arg2", object_id);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        send(json);
        return uuid;
    }

    public String delete(String topic, String object_id) {
        return delete(topic, object_id, null);
    }

    public String delete(String topic, String object_id, OnCompletionListener listener) {
        if (!isConnected())
            return "";

        String uuid = UUID.randomUUID().toString();
        if (listener != null) {
            mCallbacks.put(uuid, listener);
        }
        JSONObject json = new JSONObject();
        try {
            json.put("uuid", uuid);
            json.put("op", OP_DELETE);
            json.put("topic", topic);
            json.put("arg2", object_id);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        send(json);
        return uuid;
    }

    public String subscribe(String topic) {
        return subscribe(topic, null, null);
    }

    public String subscribe(String topic, OnCompletionListener listener) {
        return subscribe(topic, null, listener);
    }

    public String subscribe(String topic, JSONObject auth) {
        return subscribe(topic, auth, null);
    }

    public String subscribe(String topic, JSONObject auth, OnCompletionListener listener) {
        if (!isConnected())
            return "";

        String uuid = UUID.randomUUID().toString();
        if (listener != null) {
            mCallbacks.put(uuid, listener);
        }
        JSONObject json = new JSONObject();
        try {
            json.put("uuid", uuid);
            json.put("op", OP_SUBSCRIBE);
            json.put("topic", topic);
            if (auth != null) {
                json.put("auth", auth);
            }
        } catch (JSONException e1) {
            e1.printStackTrace();
        }
        send(json);
        return uuid;
    }

    public String unsubscribe(String topic) {
        return unsubscribe(topic, null);
    }

    public String unsubscribe(String topic, OnCompletionListener listener) {
        if (!isConnected())
            return "";

        String uuid = UUID.randomUUID().toString();
        if (listener != null) {
            mCallbacks.put(uuid, listener);
        }
        JSONObject json = new JSONObject();
        try {
            json.put("uuid", uuid);
            json.put("op", OP_UNSUBSCRIBE);
            json.put("topic", topic);
        } catch (JSONException e1) {
            e1.printStackTrace();
        }
        send(json);
        return uuid;
    }

    public String history(String topic, JSONObject params) {
        return history(topic, params, null);
    }

    public String history(String topic, JSONObject params, OnCompletionListener listener) {
        if (!isConnected())
            return "";

        int offset = 0;
        int limit = 0;
        JSONObject query = null;
        if (params != null) {
            try {
                offset = params.getInt("offset");
            } catch (JSONException ignored) {
            }
            try {
                limit = params.getInt("limit");
            } catch (JSONException ignored) {
            }
            try {
                query = params.getJSONObject("query");
            } catch (JSONException ignored) {
            }
        }

        String uuid = UUID.randomUUID().toString();
        if (listener != null) {
            mCallbacks.put(uuid, listener);
        }
        JSONObject json = new JSONObject();
        try {
            json.put("uuid", uuid);
            json.put("op", OP_HISTORY);
            json.put("topic", topic);
            json.put("arg1", String.valueOf(offset));
            json.put("arg2", String.valueOf(limit));
            if (query != null) {
                json.put("arg3", query);
            } else {
                json.put("arg3", "");
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        send(json);
        return uuid;
    }

    public String users(String topic) {
        return users(topic, null);
    }

    public String users(String topic, OnCompletionListener listener) {
        if (!isConnected())
            return "";

        String uuid = UUID.randomUUID().toString();
        if (listener != null) {
            mCallbacks.put(uuid, listener);
        }
        JSONObject json = new JSONObject();
        try {
            json.put("uuid", uuid);
            json.put("op", OP_USERS);
            json.put("topic", topic);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        send(json);
        return uuid;
    }

    public String topics() {
        return topics(null);
    }

    public String topics(OnCompletionListener listener) {
        if (!isConnected())
            return "";

        String uuid = UUID.randomUUID().toString();
        if (listener != null) {
            mCallbacks.put(uuid, listener);
        }
        JSONObject json = new JSONObject();
        try {
            json.put("uuid", uuid);
            json.put("op", OP_TOPICS);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        send(json);
        return uuid;
    }

    public interface OnCompletionListener {

        void onCompletion(JSONObject json);

    }

    public interface OnXservEventListener {

        void OnOpenConnection();

        void OnCloseConnection(Exception e);

        void OnErrorConnection(Exception e);

        void OnReceiveMessages(JSONObject json);

        void OnReceiveOperations(JSONObject json);

    }

}