Java tutorial
/* * Copyright (c) 2017, CESAR. * All rights reserved. * * This software may be modified and distributed under the terms * of the BSD license. See the LICENSE file for details. * * */ package br.org.cesar.knot.lib.connection; import android.support.annotation.NonNull; import com.github.nkzawa.emitter.Emitter; import com.github.nkzawa.socketio.client.Ack; import com.github.nkzawa.socketio.client.IO; import com.github.nkzawa.socketio.client.Socket; import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import org.json.JSONException; import org.json.JSONObject; import java.net.URISyntaxException; import java.util.List; import br.org.cesar.knot.lib.event.Event; import br.org.cesar.knot.lib.exception.InvalidParametersException; import br.org.cesar.knot.lib.exception.KnotException; import br.org.cesar.knot.lib.exception.SocketNotConnected; import br.org.cesar.knot.lib.model.AbstractDeviceOwner; import br.org.cesar.knot.lib.model.AbstractThingData; import br.org.cesar.knot.lib.model.AbstractThingDevice; import br.org.cesar.knot.lib.model.AbstractThingMessage; import br.org.cesar.knot.lib.model.KnotQueryData; import br.org.cesar.knot.lib.model.KnotQueryDateData; import br.org.cesar.knot.lib.model.KnotList; import br.org.cesar.knot.lib.util.DateUtils; import br.org.cesar.knot.lib.util.LogLib; /** * The type Knot mSocket io. */ final class KnotSocketIo { /** * Event used to create a new device on meshblu */ private static String EVENT_CREATE_DEVICE = "register"; /** * Event used to delete a device on meshblu */ private static String EVENT_UNREGISTER_DEVICE = "unregister"; /** * Event used to register a socket on meshblu */ private static String EVENT_IDENTITY_DEVICE = "identity"; /** * Event used to get information about the device */ private static String EVENT_WHOAMI = "whoami"; /** * Event used to update information of the device */ private static String EVENT_UPDATE_DEVICE = "update"; /** * Event used to insert a new data on server */ private static String EVENT_INSERT_DATA = "data"; /** * Event used to get all datas of specific device */ private static String EVENT_GET_DATA = "getdata"; /** * Event used to get all devices of specific device */ private static String EVENT_GET_DEVICES = "devices"; /** * Event used to get a specific device */ private static String EVENT_GET_DEVICE = "device"; /** * Event used to claim a device */ private static String EVENT_CLAIM_DEVICE = "claimdevice"; /** * Event used to register method to receive connect information */ private static String EVENT_READY = "ready"; /** * Event used to register method to receive connect information */ private static String EVENT_NOT_READY = "notReady"; /** * Event used to register method to receive messages */ private static String EVENT_MESSAGE = "message"; /** * Event used to register method to receive device information */ private static String EVENT_CONFIG = "config"; /** * First position of result of an event emitted */ private static int FIRST_EVENT_RECEIVED = 0; /** * Tag that identify an error of server */ private static String ERROR = "error"; /** * Tag that identify success of server */ private static String FROMUUID = "fromUuid"; /** * Tag that identify uuid of the device */ private static String UUID = "uuid"; /** * Tag that identify the token of the device */ private static String TOKEN = "token"; /** * Tag that represents the data of the device */ private static String DATA = "data"; /** * Tag that represents the devices of the json result */ private static String DEVICES = "devices"; /** * Tag that represents the devices of the json result */ private static String DEVICE = "device"; /** * Tag used to make the device parser */ private static String FROM = "from"; /** * Tag used to build the date query */ private static String DATE_START = "start"; /** * Tag used to build the date query */ private static String DATE_FINISH = "finish"; /** * Tag used to build the date query */ private static String LIMIT = "limit"; /** * The socket that make the communication between client and server */ private Socket mSocket; /** * Empty json */ private static final String EMPTY_JSON = "[]"; /** * Flag that show if the socket was registered */ private boolean mDeviceRegistered; /** * Manipulate json objects */ private Gson mGson; /** * Callback used to transmit all messages to client */ private Event<AbstractThingMessage> mOnMessageEventCallback; /** * Type of class used to build a new message received */ private AbstractThingMessage mMessageClass; /** * Type of class used to build a device that was modified */ private AbstractThingDevice mConfigClass; /** * Owner that has some permission to work with devices; */ private AbstractDeviceOwner mOwner; /** * Callback used to transmit all modifications to client */ private Event<AbstractThingDevice> mOnConfigEventCallback; /** * Callback used to transmit all modifications to client */ private Event<Boolean> mAuthenticateEventCallback; /** * This event is called when the server response if the device was registered */ private Emitter.Listener onReady = new Emitter.Listener() { @Override public void call(Object... args) { mDeviceRegistered = true; mAuthenticateEventCallback.onEventFinish(true); } }; /** * This event is called when the server response if the device wasn't registered */ private Emitter.Listener onNotReady = new Emitter.Listener() { @Override public void call(Object... args) { mDeviceRegistered = false; mAuthenticateEventCallback.onEventError(new KnotException(args.toString())); } }; /** * This event is called when the device receive a neu message */ private Emitter.Listener onMessageReceived = new Emitter.Listener() { @Override public void call(Object... args) { if (args != null && args.length > 0 && args[FIRST_EVENT_RECEIVED] != null) { parserToMessage(args[FIRST_EVENT_RECEIVED].toString()); } } }; /** * This event is called when the device is updated */ private Emitter.Listener onConfigDevice = new Emitter.Listener() { @Override public void call(Object... args) { if (args != null && args.length > 0 && args[FIRST_EVENT_RECEIVED] != null) { parserToConfig(args[FIRST_EVENT_RECEIVED].toString()); } } }; /** * Instantiates a new Knot mSocket io. * * @param endPoint the end point * @throws SocketNotConnected the mSocket not connected */ public KnotSocketIo(@NonNull String endPoint, @NonNull String uuidOwner, @NonNull String token) throws SocketNotConnected { this(); mOwner = new AbstractDeviceOwner(); mOwner.setUuid(uuidOwner); mOwner.setToken(token); connect(endPoint); } /** * You must call the {@link #connect(String)} to open a valid mSocket */ public KnotSocketIo() { this.mGson = new Gson(); } /** * Open a new mSocket with the meshblu * * @param endPoint endpoint of gateway * @throws SocketNotConnected if its not possible to open a mSocket */ public void connect(@NonNull String endPoint) throws SocketNotConnected { try { mSocket = IO.socket(endPoint); mSocket.on(EVENT_READY, onReady); mSocket.on(EVENT_NOT_READY, onNotReady); mSocket.on(EVENT_MESSAGE, onMessageReceived); mSocket.on(EVENT_CONFIG, onConfigDevice); mSocket.connect(); } catch (URISyntaxException e) { LogLib.printE("Its not possible to connect on mSocket", e); } } /** * Disconnect and invalidate the current mSocket. You must call {@link #connect(String)} to open a valid mSocket * before to do any action on meshblu */ public void disconnect() { if (isSocketConnected()) { mSocket.disconnect(); mSocket = null; } } /** * Generate a new Device in Meshblu instance * * @param device model sample to create a new one. Basically this device model * contains attributes that will be saved into Meshblu. * Please note that uuid and token will always * be generated by Meshblu (please see AbstractThingDevice). * It is important set the custom attribute for your classes * @param callbackResult Callback for this method * @return New device with meshblu token and uuid values * @throws KnotException * @throws SocketNotConnected * @see AbstractThingDevice * <p> * Check the reference on @see <a href="https://meshblu-socketio.readme.io/docs/register</a> */ public <T extends AbstractThingDevice> void createNewDevice(final T device, final Event<T> callbackResult) throws SocketNotConnected, JSONException { if (isSocketConnected() && device != null) { device.owner = mOwner.getUuid(); String json = mGson.toJson(device); JSONObject deviceToSend = new JSONObject(json); mSocket.emit(EVENT_CREATE_DEVICE, deviceToSend, new Ack() { @Override public void call(Object... args) { // check if it's necessary to return the result of event if (callbackResult != null) { Object firstPosition; if ((firstPosition = args[FIRST_EVENT_RECEIVED]) != null) { final String json = firstPosition.toString(); final T result = (T) mGson.fromJson(json, device.getClass()); //call the callback callbackResult.onEventFinish(result); } else { callbackResult.onEventError(new KnotException()); } } } }); } else { throw new SocketNotConnected("Socket not ready or connected"); } } /** * Turns the device belongs to someone. When a device is created in * Meshblu, it is an orphan device. In other words, everyone can made any * changes on this device. After claim a device, only the * owner can delete or update it. * Note: In Meshblu, the owner for one device IS another device. * * @param device the identifier of device (uuid) * @param callbackResult Callback for this method * @throws KnotException * @throws SocketNotConnected <p> * Check the reference on @see <a https://meshblu-socketio.readme.io/docs/unregister</a> */ public <T extends AbstractThingDevice> void deleteDevice(final T device, final Event<T> callbackResult) throws SocketNotConnected { if (isSocketRegistered() && isSocketConnected() && device != null) { JSONObject deviceToDelete = getNecessaryDeviceInformation(device); if (deviceToDelete != null) { mSocket.emit(EVENT_UNREGISTER_DEVICE, deviceToDelete, new Ack() { @Override public void call(Object... args) { //Get First element of the array if (args.length > 0 && args[FIRST_EVENT_RECEIVED] != null) { JsonElement jsonElement = new JsonParser().parse(args[FIRST_EVENT_RECEIVED].toString()); JsonObject jsonObject = jsonElement.getAsJsonObject(); if (jsonObject.get(ERROR) != null) { callbackResult.onEventError(new KnotException(jsonObject.get(ERROR).toString())); } else if (jsonObject.get(FROMUUID) != null) { callbackResult.onEventFinish(device); } else { callbackResult.onEventError(new KnotException("Unknown error")); } } else { callbackResult.onEventError(new KnotException("Failed to delete file")); } } }); } } else { throw new SocketNotConnected("Socket not ready or connected"); } } /** * The API Needs to call this method to authenticate a device with the socket communication. * * @param callbackResult Callback for this method * @throws KnotException * @throws SocketNotConnected * @throws InvalidParametersException Check the reference on * <p> * @see <a https://meshblu-socketio.readme.io/docs/identity</a> */ public <T extends AbstractThingDevice> void authenticateDevice(final Event<Boolean> callbackResult) throws SocketNotConnected, InvalidParametersException { if (isSocketConnected()) { if (mOwner != null && callbackResult != null) { mAuthenticateEventCallback = callbackResult; String json = mGson.toJson(mOwner); JSONObject deviceToAutheticate = null; try { deviceToAutheticate = new JSONObject(json); } catch (JSONException e) { callbackResult.onEventError(new KnotException(e.getMessage())); } mSocket.emit(EVENT_IDENTITY_DEVICE, deviceToAutheticate); } else { throw new InvalidParametersException("Invalid parameters"); } } else { throw new SocketNotConnected("Socket not ready or connected"); } } /** * Get all information regarding the device. * * @param callbackResult Callback for this method * @param device the device * @throws KnotException * @throws SocketNotConnected * @throws InvalidParametersException Check the reference on * <p> * @see <a https://meshblu-socketio.readme.io/docs/whoami </a> */ // public <T extends AbstractThingDevice> void whoAmI(final T device, final Event<T> callbackResult) throws JSONException, SocketNotConnected, InvalidParametersException { if (isSocketConnected() && isSocketRegistered()) { if (device != null && callbackResult != null) { JSONObject whoAmi = getNecessaryDeviceInformation(device); if (whoAmi != null) { mSocket.emit(EVENT_WHOAMI, whoAmi, new Ack() { @Override public void call(Object... args) { try { T result = (T) mGson.fromJson(args[FIRST_EVENT_RECEIVED].toString(), device.getClass()); callbackResult.onEventFinish(result); } catch (Exception e) { callbackResult.onEventError(new KnotException()); } } }); } } else { throw new InvalidParametersException("Invalid parameters"); } } else { throw new SocketNotConnected("Socket not ready or connected"); } } /** * Update an existent device * * @param device the identifier of device (uuid) * @param callbackResult Callback for this method * @throws KnotException * @throws SocketNotConnected * @throws InvalidParametersException <p> * Check the reference on @see <a https://meshblu-socketio.readme.io/docs/update</a> */ public <T extends AbstractThingDevice> void updateDevice(final T device, final Event<T> callbackResult) throws SocketNotConnected, InvalidParametersException { if (isSocketConnected() && isSocketRegistered()) { if (device != null && callbackResult != null) { String json = mGson.toJson(device); JSONObject deviceToUpdate = null; try { deviceToUpdate = new JSONObject(json); } catch (JSONException e) { throw new InvalidParametersException("Invalid parameters. Please, check your device object"); } if (deviceToUpdate != null) { mSocket.emit(EVENT_UPDATE_DEVICE, deviceToUpdate, new Ack() { @Override public void call(Object... args) { //Get First element of the array if (args.length > 0 && args[FIRST_EVENT_RECEIVED] != null) { JsonElement jsonElement = new JsonParser() .parse(args[FIRST_EVENT_RECEIVED].toString()); JsonObject jsonObject = jsonElement.getAsJsonObject(); if (jsonObject.get(ERROR) != null) { callbackResult .onEventError(new KnotException(jsonObject.get(ERROR).toString())); } else { T result = (T) mGson.fromJson(args[FIRST_EVENT_RECEIVED].toString(), device.getClass()); callbackResult.onEventFinish(result); } } else { callbackResult.onEventError(new KnotException("Failed to update file")); } } }); } } else { throw new InvalidParametersException("Invalid parameters"); } } else { throw new SocketNotConnected("Socket not ready or connected"); } } /** * Method used to claim a specif device; * * @param device device Wanted * @param callbackResult Callback for this method * @throws SocketNotConnected * @throws InvalidParametersException * @throws JSONException */ public <T extends AbstractThingDevice> void claimDevice(final T device, final Event<Boolean> callbackResult) throws SocketNotConnected, InvalidParametersException, JSONException { if (isSocketConnected() && isSocketRegistered()) { if (device != null && callbackResult != null) { JSONObject uuidDevice = new JSONObject(); uuidDevice.put(UUID, device); mSocket.emit(EVENT_CLAIM_DEVICE, uuidDevice, new Ack() { @Override public void call(Object... args) { if (args != null && args.length > 0) { JsonElement jsonElement = new JsonParser().parse(args[FIRST_EVENT_RECEIVED].toString()); JsonObject jsonObject = jsonElement.getAsJsonObject(); if (jsonObject.get(ERROR) != null && !jsonObject.get(ERROR).isJsonNull()) { callbackResult.onEventError(new KnotException(jsonObject.get(ERROR).toString())); } else { callbackResult.onEventFinish(true); } } else { callbackResult.onEventError(new KnotException("Error in reading the result")); } } }); } else { throw new InvalidParametersException("Invalid parameters"); } } else { throw new SocketNotConnected("Socket not ready or connected"); } } /** * This method returns a instance of the device * * @param typeClass The specific genericized type of src. * @param uuid The device identification to find a device on server * @param callbackResult Callback for this method * @param <T> the type of the desired object * @throws JSONException */ public <T extends AbstractThingDevice> void getDevice(final T typeClass, String uuid, final Event<T> callbackResult) throws JSONException, InvalidParametersException, SocketNotConnected { if (isSocketConnected() && isSocketRegistered()) { if (typeClass != null && callbackResult != null && uuid != null) { JSONObject uuidDevice = new JSONObject(); uuidDevice.put(UUID, uuid); mSocket.emit(EVENT_GET_DEVICE, uuidDevice, new Ack() { @Override public void call(Object... args) { if (args != null && args.length > 0) { JsonElement jsonElement = new JsonParser().parse(args[FIRST_EVENT_RECEIVED].toString()); JsonObject jsonObject = jsonElement.getAsJsonObject(); if (jsonObject.get(ERROR) != null && !jsonObject.get(ERROR).isJsonNull()) { callbackResult.onEventError(new KnotException(jsonObject.get(ERROR).toString())); } else if (jsonObject.get(FROM) != null) { JsonObject json = jsonObject.get(DEVICE).getAsJsonObject(); try { T result = (T) mGson.fromJson(json.toString(), typeClass.getClass()); callbackResult.onEventFinish(result); } catch (Exception e) { callbackResult.onEventError(new KnotException("Error in reading the result")); } } else { callbackResult.onEventError(new KnotException("Unknown error")); } } else { callbackResult.onEventError(new KnotException("Error in reading the result")); } } }); } else { throw new InvalidParametersException("Invalid parameters"); } } else { throw new SocketNotConnected("Socket not ready or connected"); } } /** * This method return a list devices of the specific owner * * @param typeThing Generic type of list. * @param query Query to find devices * @param callbackResult List of devices * @throws KnotException <p> * @see <ahttps://meshblu-socketio.readme.io/docs/devices </a> */ public <T extends AbstractThingDevice> void getDeviceList(final KnotList<T> typeThing, JSONObject query, final Event<List<T>> callbackResult) throws KnotException, SocketNotConnected, InvalidParametersException { if (isSocketConnected() && isSocketRegistered()) { if (typeThing != null && callbackResult != null) { if (query == null) { query = new JSONObject(); } mSocket.emit(EVENT_GET_DEVICES, query, new Ack() { @Override public void call(Object... args) { List<T> result = null; try { JsonElement jsonElement = new JsonParser().parse(args[FIRST_EVENT_RECEIVED].toString()); JsonObject jsonObject = jsonElement.getAsJsonObject(); if (jsonObject.get(ERROR) != null && !jsonObject.get(ERROR).isJsonNull()) { callbackResult.onEventError(new KnotException(jsonObject.get(ERROR).toString())); return; } JsonArray jsonArray = jsonElement.getAsJsonObject().getAsJsonArray(DEVICES); if (jsonArray != null || jsonArray.size() > 0) { result = mGson.fromJson(jsonArray, typeThing); } else { result = mGson.fromJson(EMPTY_JSON, typeThing); } callbackResult.onEventFinish((List<T>) result); } catch (Exception e) { callbackResult.onEventError(new KnotException(e)); } } }); } else { throw new InvalidParametersException("Invalid parameters"); } } else { throw new SocketNotConnected("Socket not ready or connected"); } } /** * Create data for one device. If the device has an owner, it is necessary that the owner * param be the same of the device owner. * * @param data data that will be created for device * @param callbackResult Callback for this method * @throws KnotException * @throws SocketNotConnected * @throws InvalidParametersException */ public <T extends AbstractThingData> void createData(final String uuid, final T data, final Event<T> callbackResult) throws JSONException, InvalidParametersException, SocketNotConnected { if (isSocketConnected() && isSocketRegistered()) { if (data != null && callbackResult != null) { String json = mGson.toJson(data); JSONObject dataToSend = new JSONObject(json); dataToSend.put(UUID, uuid); mSocket.emit(EVENT_INSERT_DATA, dataToSend, new Ack() { @Override public void call(Object... args) { //When the result is success, the response return empty. if (args != null && args.length == 0) { callbackResult.onEventFinish(data); } else { callbackResult.onEventError(new KnotException("Invalid response")); } } }); } else { throw new InvalidParametersException("Invalid parameters"); } } else { throw new SocketNotConnected("Socket not ready or connected"); } } /** * Get all data of the specific device * * @param type List of abstracts objects * @param uuid UUid of device * @param deviceToken token of the device * @param knotQueryData Date query * @param callbackResult Callback for this method * @throws InvalidParametersException * @throws SocketNotConnected */ public <T extends AbstractThingData> void getData(final KnotList<T> type, String uuid, String deviceToken, KnotQueryData knotQueryData, final Event<List<T>> callbackResult) throws InvalidParametersException, SocketNotConnected { if (isSocketConnected() && isSocketRegistered() && knotQueryData != null) { if (uuid != null && callbackResult != null) { JSONObject dataToSend = new JSONObject(); int maxNumberOfItem = -1; try { dataToSend.put(UUID, uuid); dataToSend.put(TOKEN, deviceToken); if (knotQueryData.getStartDate() != null) { dataToSend.put(DATE_START, DateUtils.getTimeStamp(knotQueryData.getStartDate())); } if (knotQueryData.getFinishDate() != null) { dataToSend.put(DATE_FINISH, DateUtils.getTimeStamp(knotQueryData.getFinishDate())); } if (knotQueryData.getLimit() > 0) { maxNumberOfItem = knotQueryData.getLimit(); } dataToSend.put(LIMIT, maxNumberOfItem); } catch (JSONException e) { callbackResult.onEventError(new KnotException()); } mSocket.emit(EVENT_GET_DATA, dataToSend, new Ack() { @Override public void call(Object... args) { if (args != null && args.length > 0) { List<T> result = null; JsonElement jsonElement = new JsonParser().parse(args[FIRST_EVENT_RECEIVED].toString()); JsonArray jsonArray = jsonElement.getAsJsonObject().getAsJsonArray(DATA); if (jsonArray != null && jsonArray.size() > 0) { result = mGson.fromJson(jsonArray.toString(), type); } else { result = mGson.fromJson(EMPTY_JSON, type); } callbackResult.onEventFinish(result); } else { callbackResult.onEventError(new KnotException()); } } }); } else { throw new InvalidParametersException("Invalid parameters"); } } else { throw new SocketNotConnected("Socket not ready or connected"); } } /** * This method sends a message to a device list of your choice. * * @param message AbstractThingMessage that will be sent * @param <T> Type of Object * @throws InvalidParametersException * @throws SocketNotConnected * @throws JSONException * @see <a> https://meshblu-socketio.readme.io/docs/message </a> */ public <T extends AbstractThingMessage> void sendMessage(final T message) throws InvalidParametersException, SocketNotConnected, JSONException { if (isSocketConnected() && isSocketRegistered()) { if (message != null) { String json = mGson.toJson(message); JSONObject dataToSend = new JSONObject(json); mSocket.emit(EVENT_MESSAGE, dataToSend); } else { throw new InvalidParametersException("Invalid parameters"); } } else { throw new SocketNotConnected("Socket not ready or connected"); } } /** * Set callback to receive message to your device * * @param messageEventCallback Callback to receive message */ public <T extends AbstractThingMessage> void setCallbackToMessageEvent( final Event<AbstractThingMessage> messageEventCallback, T classOfT) { mOnMessageEventCallback = messageEventCallback; mMessageClass = classOfT; } /** * Set callback to capture information of the your device * * @param configEventeCallback Callback to receive device information */ public <T extends AbstractThingDevice> void setCallbackToConfigEvent( final Event<AbstractThingDevice> configEventeCallback, T classOfT) { mOnConfigEventCallback = configEventeCallback; mConfigClass = classOfT; } /** * Method used to make a parser from json to AbstractThingMessage. * * @param json Json that has message information * @param <T> type of classe */ private <T extends AbstractThingMessage> void parserToMessage(String json) { if (json != null && mOnMessageEventCallback != null && mMessageClass != null) { T result = (T) mGson.fromJson(json, mMessageClass.getClass()); if (result != null) { mOnMessageEventCallback.onEventFinish(result); } } } /** * Method used to make a parser from json to AbstractThingDevice. * * @param json Json that has device's information * @param <T> type of classe */ private <T extends AbstractThingDevice> void parserToConfig(String json) { try { if (json != null) { T result = (T) mGson.fromJson(json, AbstractThingDevice.class); if (result != null) { mOnConfigEventCallback.onEventFinish(result); } } } catch (Exception e) { LogLib.printD(e.getMessage()); } } /** * Building a JsonObject to use in some methods * * @param device that contains all information * @return JsonObject */ private <T extends AbstractThingDevice> JSONObject getNecessaryDeviceInformation(T device) { JSONObject deviceInformation = new JSONObject(); try { deviceInformation.put(UUID, device.getUuid()); deviceInformation.put(TOKEN, device.getToken()); } catch (JSONException e) { e.printStackTrace(); } return deviceInformation; } /** * Is mSocket connected boolean. * * @return the boolean */ public boolean isSocketConnected() { return mSocket != null && mSocket.connected(); } /** * Is mSocket registered on meshblu . * * @return the boolean */ public boolean isSocketRegistered() { return isSocketConnected() && mDeviceRegistered; } }