![]() |
Hubiquitus Android
0.3
Android client for hubiquitus protocol
|
00001 /* 00002 * Copyright (c) Novedia Group 2012. 00003 * 00004 * This file is part of Hubiquitus. 00005 * 00006 * Hubiquitus is free software: you can redistribute it and/or modify 00007 * it under the terms of the GNU General Public License as published by 00008 * the Free Software Foundation, either version 3 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * Hubiquitus is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License 00017 * along with Hubiquitus. If not, see <http://www.gnu.org/licenses/>. 00018 */ 00019 00020 package org.hubiquitus.hapi.client; 00021 00022 import java.util.Hashtable; 00023 import java.util.Random; 00024 00025 import org.hubiquitus.hapi.hStructures.ConnectionError; 00026 import org.hubiquitus.hapi.hStructures.ConnectionStatus; 00027 import org.hubiquitus.hapi.hStructures.HAck; 00028 import org.hubiquitus.hapi.hStructures.HAckValue; 00029 import org.hubiquitus.hapi.hStructures.HAlert; 00030 import org.hubiquitus.hapi.hStructures.HCommand; 00031 import org.hubiquitus.hapi.hStructures.HConvState; 00032 import org.hubiquitus.hapi.hStructures.HJsonObj; 00033 import org.hubiquitus.hapi.hStructures.HMeasure; 00034 import org.hubiquitus.hapi.hStructures.HMessage; 00035 import org.hubiquitus.hapi.hStructures.HMessageOptions; 00036 import org.hubiquitus.hapi.hStructures.HOptions; 00037 import org.hubiquitus.hapi.hStructures.HResult; 00038 import org.hubiquitus.hapi.hStructures.HStatus; 00039 import org.hubiquitus.hapi.hStructures.ResultStatus; 00040 import org.hubiquitus.hapi.structures.JabberID; 00041 import org.hubiquitus.hapi.transport.HTransport; 00042 import org.hubiquitus.hapi.transport.HTransportDelegate; 00043 import org.hubiquitus.hapi.transport.HTransportOptions; 00044 import org.hubiquitus.hapi.transport.socketio.HTransportSocketio; 00045 import org.hubiquitus.hapi.transport.xmpp.HTransportXMPP; 00046 import org.hubiquitus.hapi.util.HJsonDictionnary; 00047 import org.hubiquitus.hapi.util.HUtil; 00048 import org.json.JSONObject; 00049 00050 import exceptions.MissingAttrException; 00051 00052 00058 public class HClient { 00059 00060 private ConnectionStatus connectionStatus = ConnectionStatus.DISCONNECTED; /* only connecting, connected, diconnecting, disconnected */ 00061 @SuppressWarnings("unused") 00062 private HOptions options = null; 00063 private HTransportOptions transportOptions = null; 00064 private HTransport transport; 00065 00066 private HStatusDelegate statusDelegate = null; 00067 private HMessageDelegate messageDelegate = null; 00068 private Hashtable<String, HCommandDelegate> commandsDelegates = new Hashtable<String, HCommandDelegate>(); 00069 00070 private TransportDelegate transportDelegate= new TransportDelegate(); 00071 00072 public HClient() { 00073 transportOptions = new HTransportOptions(); 00074 } 00075 00083 public void connect(String publisher, String password, HOptions options) { 00084 boolean shouldConnect = false; 00085 boolean connInProgress = false; 00086 boolean disconInProgress = false; 00087 00088 //synchronize connection status updates to make sure, we have one connect at a time 00089 synchronized (this) { 00090 if (this.connectionStatus == ConnectionStatus.DISCONNECTED) { 00091 shouldConnect = true; 00092 00093 //update connection status 00094 connectionStatus = ConnectionStatus.CONNECTING; 00095 } else if(this.connectionStatus == ConnectionStatus.CONNECTING) { 00096 connInProgress = true; 00097 } else if(this.connectionStatus == ConnectionStatus.DISCONNECTING) { 00098 disconInProgress = true; 00099 } 00100 } 00101 00102 if (shouldConnect) { //if not connected, then connect 00103 00104 //notify connection 00105 this.notifyStatus(ConnectionStatus.CONNECTING, ConnectionError.NO_ERROR, null); 00106 00107 //fill HTransportOptions 00108 try { 00109 this.fillHTransportOptions(publisher, password, options); 00110 } catch (Exception e) { 00111 //stop connecting if filling error 00112 this.notifyStatus(ConnectionStatus.DISCONNECTED, ConnectionError.JID_MALFORMAT, e.getMessage()); 00113 return; 00114 } 00115 00116 //choose transport layer 00117 if(options.getTransport().equals("socketio")) { 00118 /*if (this.transport != null) { //check if other transport mode connect 00119 this.transport.disconnect(); 00120 }*/ 00121 if (this.transport == null || (this.transport.getClass() != HTransportSocketio.class)) { 00122 this.transport = new HTransportSocketio(); 00123 } 00124 this.transport.connect(transportDelegate, this.transportOptions); 00125 } else { 00126 /*if (this.transport != null) { //check if other transport mode connect 00127 this.transport.disconnect(); 00128 }*/ 00129 this.transport = new HTransportXMPP(); 00130 this.transport.connect(transportDelegate, this.transportOptions); 00131 } 00132 } else { 00133 if (connInProgress) { 00134 notifyStatus(ConnectionStatus.CONNECTING, ConnectionError.CONN_PROGRESS, null); 00135 } else if (disconInProgress) { 00136 //updateStatus(ConnectionStatus.DISCONNECTING, ConnectionError.ALREADY_CONNECTED, null); 00137 } else { 00138 notifyStatus(ConnectionStatus.CONNECTED, ConnectionError.ALREADY_CONNECTED, null); 00139 } 00140 } 00141 } 00142 00143 public void disconnect() { 00144 boolean shouldDisconnect = false; 00145 boolean connectInProgress = false; 00146 synchronized (this) { 00147 if (this.connectionStatus == ConnectionStatus.CONNECTED) { 00148 shouldDisconnect = true; 00149 //update connection status 00150 connectionStatus = ConnectionStatus.DISCONNECTING; 00151 } else if(this.connectionStatus == ConnectionStatus.CONNECTING) { 00152 connectInProgress = true; 00153 } 00154 } 00155 00156 if(shouldDisconnect) { 00157 notifyStatus(ConnectionStatus.DISCONNECTING, ConnectionError.NO_ERROR, null); 00158 transport.disconnect(); 00159 } else if (connectInProgress) { 00160 notifyStatus(ConnectionStatus.CONNECTING, ConnectionError.CONN_PROGRESS, "Can't disconnect while a connection is in progress"); 00161 } else { 00162 notifyStatus(ConnectionStatus.DISCONNECTED, ConnectionError.NOT_CONNECTED, null); 00163 } 00164 00165 00166 } 00167 00172 public void onStatus(HStatusDelegate statusDelgate) { 00173 this.statusDelegate = statusDelgate; 00174 } 00175 00180 public void onMessage(HMessageDelegate messageDelegate) { 00181 this.messageDelegate = messageDelegate; 00182 } 00183 00188 public ConnectionStatus status() { 00189 return this.connectionStatus; 00190 } 00191 00198 public void command(HCommand cmd, HCommandDelegate commandDelegate) { 00199 if(this.connectionStatus == ConnectionStatus.CONNECTED && cmd != null) { 00200 String reqid = null; 00201 reqid = cmd.getReqid(); 00202 if(reqid == null) { 00203 Random rand = new Random(); 00204 reqid = "javaCmd:" + rand.nextInt(); 00205 cmd.setReqid(reqid); 00206 } 00207 00208 if(cmd.getSender() == null) { 00209 cmd.setSender(transportOptions.getJid().getFullJID()); 00210 } 00211 00212 if(cmd.getTransient() == null) { 00213 cmd.setTransient(true); 00214 } 00215 00216 if(cmd.getEntity() != null) { 00217 if (commandDelegate != null) { 00218 commandsDelegates.put(reqid, commandDelegate); 00219 } 00220 transport.sendObject(cmd.toJSON()); 00221 } else { 00222 this.notifyResultError(cmd.getReqid(), cmd.getCmd(), ResultStatus.MISSING_ATTR, "Entity not found"); 00223 } 00224 } else if (cmd == null) { 00225 this.notifyResultError(null, null, ResultStatus.MISSING_ATTR, "Provided cmd is null", commandDelegate); 00226 } else { 00227 this.notifyResultError(cmd.getReqid(), cmd.getCmd(), ResultStatus.NOT_CONNECTED, null, commandDelegate); 00228 } 00229 } 00230 00239 public void subscribe(String chid, HCommandDelegate commandDelegate) { 00240 HJsonDictionnary params = new HJsonDictionnary(); 00241 params.put("chid", chid); 00242 HCommand cmd = new HCommand(transportOptions.getHserverService(), "hsubscribe", params); 00243 this.command(cmd, commandDelegate); 00244 } 00245 00253 public void unsubscribe(String chid, HCommandDelegate commandDelegate) { 00254 HJsonDictionnary params = new HJsonDictionnary(); 00255 params.put("chid", chid); 00256 HCommand cmd = new HCommand(transportOptions.getHserverService(), "hunsubscribe", params); 00257 this.command(cmd, commandDelegate); 00258 } 00259 00266 public void publish(HMessage message, HCommandDelegate commandDelegate) { 00267 //fill mandatory fields 00268 String msgid = message.getMsgid(); 00269 if(msgid == null) { 00270 Random rand = new Random(); 00271 msgid = "javaCmd:" + rand.nextInt(); 00272 message.setMsgid(msgid); 00273 } 00274 00275 String convid = message.getConvid(); 00276 if(convid == null) { 00277 message.setConvid(msgid); 00278 } 00279 00280 message.setConvid(convid); 00281 00282 if(message.getPublisher() == null) { 00283 message.setPublisher(this.transportOptions.getJid().getBareJID()); 00284 } 00285 00286 HCommand cmd = new HCommand(transportOptions.getHserverService(), "hpublish", message); 00287 this.command(cmd, commandDelegate); 00288 } 00302 public void getLastMessages(String chid, int nbLastMsg, HCommandDelegate commandDelegate) { 00303 HJsonDictionnary params = new HJsonDictionnary(); 00304 params.put("chid", chid); 00305 if(nbLastMsg > 0) { 00306 params.put("nbLastMsg", nbLastMsg); 00307 } 00308 HCommand cmd = new HCommand(transportOptions.getHserverService(), "hgetlastmessages", params); 00309 this.command(cmd, commandDelegate); 00310 } 00311 00318 public void getLastMessages(String chid, HCommandDelegate commandDelegate) { 00319 this.getLastMessages(chid,-1, commandDelegate); 00320 } 00321 00329 public void getSubscriptions(HCommandDelegate commandDelegate) { 00330 HCommand cmd = new HCommand(transportOptions.getHserverService(), "hgetsubscriptions", null); 00331 this.command(cmd, commandDelegate); 00332 } 00333 00342 public void getThread(String chid, String convid, HCommandDelegate commandDelegate) { 00343 HJsonDictionnary params = new HJsonDictionnary(); 00344 String cmdName = "hgetthread"; 00345 00346 //check mandatory fields 00347 if (chid == null || chid.length() <= 0) { 00348 notifyResultError(null, cmdName, ResultStatus.MISSING_ATTR, "Chid is missing", commandDelegate); 00349 return; 00350 } 00351 00352 if (convid == null || convid.length() <= 0) { 00353 notifyResultError(null, cmdName, ResultStatus.MISSING_ATTR, "Convid is missing", commandDelegate); 00354 return; 00355 } 00356 00357 params.put("chid", chid); 00358 params.put("convid", convid); 00359 00360 HCommand cmd = new HCommand(transportOptions.getHserverService(), cmdName, params); 00361 this.command(cmd, commandDelegate); 00362 } 00363 00372 public void getThreads(String chid, String status, HCommandDelegate commandDelegate) { 00373 HJsonDictionnary params = new HJsonDictionnary(); 00374 String cmdName = "hgetthreads"; 00375 00376 //check mandatory fields 00377 if (chid == null || chid.length() <= 0) { 00378 notifyResultError(null, cmdName, ResultStatus.MISSING_ATTR, "Chid is missing", commandDelegate); 00379 return; 00380 } 00381 00382 if (status == null || status.length() <= 0) { 00383 notifyResultError(null, cmdName, ResultStatus.MISSING_ATTR, "Status is missing", commandDelegate); 00384 return; 00385 } 00386 00387 params.put("chid", chid); 00388 params.put("status", status); 00389 00390 HCommand cmd = new HCommand(transportOptions.getHserverService(), cmdName, params); 00391 this.command(cmd, commandDelegate); 00392 } 00393 00394 /* Builder */ 00395 00406 public HMessage buildMessage(String chid, String type, HJsonObj payload, HMessageOptions options) throws MissingAttrException { 00407 00408 //check for required attributes 00409 if (chid == null || chid.length() <= 0) { 00410 throw new MissingAttrException("chid"); 00411 } 00412 00413 //build the message 00414 HMessage hmessage = new HMessage(); 00415 00416 hmessage.setChid(chid); 00417 hmessage.setType(type); 00418 if(options != null) { 00419 hmessage.setConvid(options.getConvid()); 00420 hmessage.setPriority(options.getPriority()); 00421 hmessage.setRelevance(options.getRelevance()); 00422 hmessage.setTransient(options.getTransient()); 00423 hmessage.setLocation(options.getLocation()); 00424 hmessage.setAuthor(options.getAuthor()); 00425 hmessage.setHeaders(options.getHeaders()); 00426 } 00427 00428 if(transportOptions != null && transportOptions.getJid() != null) { 00429 hmessage.setPublisher(transportOptions.getJid().getBareJID()); 00430 } else { 00431 hmessage.setPublisher(null); 00432 } 00433 00434 hmessage.setPayload(payload); 00435 00436 return hmessage; 00437 } 00438 00448 public HMessage buildConvState(String chid, String convid, String status, HMessageOptions options) throws MissingAttrException { 00449 00450 //check for required attributes 00451 if (chid == null || chid.length() <= 0) { 00452 throw new MissingAttrException("chid"); 00453 } 00454 00455 if (convid == null || convid.length() <= 0) { 00456 throw new MissingAttrException("convid"); 00457 } 00458 00459 if (status == null || status.length() <= 0) { 00460 throw new MissingAttrException("status"); 00461 } 00462 00463 HMessage hmessage = new HMessage(); 00464 00465 HConvState hconvstate = new HConvState(); 00466 hconvstate.setStatus(status); 00467 00468 hmessage = buildMessage(chid, "hConvState", hconvstate, options); 00469 hmessage.setConvid(convid); 00470 00471 return hmessage; 00472 } 00473 00483 public HMessage buildAck(String chid, String ackid,HAckValue ack, HMessageOptions options) throws MissingAttrException { 00484 //check for required attributes 00485 if (chid == null || chid.length() <= 0) { 00486 throw new MissingAttrException("chid"); 00487 } 00488 00489 //check for required attributes 00490 if (ackid == null || ackid.length() <= 0) { 00491 throw new MissingAttrException("ackid"); 00492 } 00493 00494 //check for required attributes 00495 if (ack == null) { 00496 throw new MissingAttrException("ack"); 00497 } 00498 00499 HMessage hmessage = new HMessage(); 00500 00501 HAck hack = new HAck(); 00502 hack.setAckid(ackid); 00503 hack.setAck(ack); 00504 hmessage = buildMessage(chid, "hAck", hack, options); 00505 00506 return hmessage; 00507 } 00508 00517 public HMessage buildAlert(String chid, String alert, HMessageOptions options) throws MissingAttrException { 00518 //check for required attributes 00519 if (chid == null || chid.length() <= 0) { 00520 throw new MissingAttrException("chid"); 00521 } 00522 00523 //check for required attributes 00524 if (alert == null || alert.length() <= 0) { 00525 throw new MissingAttrException("chid"); 00526 } 00527 00528 HMessage hmessage = new HMessage(); 00529 00530 HAlert halert = new HAlert(); 00531 halert.setAlert(alert); 00532 00533 hmessage = buildMessage(chid, "hAlert", halert, options); 00534 00535 return hmessage; 00536 } 00537 00547 public HMessage buildMeasure(String chid, String value, String unit, HMessageOptions options) throws MissingAttrException { 00548 //check for required attributes 00549 if (chid == null || chid.length() <= 0) { 00550 throw new MissingAttrException("chid"); 00551 } 00552 00553 //check for required attributes 00554 if (value == null || value.length() <= 0) { 00555 throw new MissingAttrException("value"); 00556 } 00557 00558 //check for required attributes 00559 if (unit == null || unit.length() <= 0) { 00560 throw new MissingAttrException("unit"); 00561 } 00562 00563 HMessage hmessage = new HMessage(); 00564 00565 HMeasure hmeasure = new HMeasure(); 00566 hmeasure.setValue(value); 00567 hmeasure.setUnit(unit); 00568 hmessage = buildMessage(chid, "hMeasure", hmeasure, options); 00569 00570 return hmessage; 00571 } 00572 /* HTransportCallback functions */ 00573 00583 private void fillHTransportOptions(String publisher, String password, HOptions options) throws Exception { 00584 JabberID jid = new JabberID(publisher); 00585 00586 this.transportOptions.setJid(jid); 00587 this.transportOptions.setPassword(password); 00588 this.transportOptions.setHserver(options.getHserver()); 00589 00590 //by default we user server host rather than publish host if defined 00591 if (options.getServerHost() != null ) { 00592 this.transportOptions.setServerHost(options.getServerHost()); 00593 } else { 00594 this.transportOptions.setServerHost(jid.getDomain()); 00595 } 00596 00597 this.transportOptions.setServerPort(options.getServerPort()); 00598 //for endpoints, pick one randomly and fill htransport options 00599 if (options.getEndpoints().size() > 0) { 00600 int endpointIndex = HUtil.pickIndex(options.getEndpoints()); 00601 String endpoint = options.getEndpoints().get(endpointIndex); 00602 00603 this.transportOptions.setEndpointHost(HUtil.getHost(endpoint)); 00604 this.transportOptions.setEndpointPort(HUtil.getPort(endpoint)); 00605 this.transportOptions.setEndpointPath(HUtil.getPath(endpoint)); 00606 } else { 00607 this.transportOptions.setEndpointHost(null); 00608 this.transportOptions.setEndpointPort(0); 00609 this.transportOptions.setEndpointPath(null); 00610 } 00611 } 00612 00620 private void notifyStatus(ConnectionStatus status, ConnectionError error, String errorMsg) { 00621 try { 00622 if (this.statusDelegate != null) { 00623 connectionStatus = status; 00624 00625 //create structure 00626 final HStatus hstatus = new HStatus(); 00627 hstatus.setStatus(status); 00628 hstatus.setErrorCode(error); 00629 hstatus.setErrorMsg(errorMsg); 00630 00631 //return status asynchronously 00632 (new Thread(new Runnable() { 00633 public void run() { 00634 try { 00635 statusDelegate.onStatus(hstatus); 00636 } catch (Exception e) { 00637 // TODO: Add a message to message logger 00638 e.printStackTrace(); 00639 } 00640 } 00641 })).start(); 00642 } 00643 } catch (Exception e) { 00644 e.printStackTrace(); 00645 // TODO: Add a message to message logger 00646 } 00647 } 00648 00653 private void notifyMessage(final HMessage message) { 00654 try { 00655 if (this.messageDelegate != null) { 00656 00657 //return message asynchronously 00658 (new Thread(new Runnable() { 00659 public void run() { 00660 try { 00661 messageDelegate.onMessage(message); 00662 } catch (Exception e) { 00663 // TODO: Add a message to message logger 00664 e.printStackTrace(); 00665 } 00666 } 00667 })).start(); 00668 } 00669 } catch (Exception e) { 00670 e.printStackTrace(); 00671 // TODO: Add a message to message logger 00672 } 00673 } 00674 00679 private void notifyResult(final HResult result, final HCommandDelegate commandDelegate) { 00680 try { 00681 if (commandDelegate != null) { 00682 00683 //return result asynchronously 00684 (new Thread(new Runnable() { 00685 public void run() { 00686 try { 00687 commandDelegate.onResult(result); 00688 } catch (Exception e) { 00689 // TODO: Add a message to message logger 00690 e.printStackTrace(); 00691 } 00692 } 00693 })).start(); 00694 } else { 00695 // TODO: add a message to logger in debug mode to know which results are dropped 00696 } 00697 } catch (Exception e) { 00698 e.printStackTrace(); 00699 // TODO: Add a message to message logger 00700 } 00701 } 00702 00709 private void notifyResult(final HResult result) { 00710 HCommandDelegate commandDelegate = commandsDelegates.get(result.getReqid()); 00711 notifyResult(result, commandDelegate); 00712 } 00713 00719 private void notifyResultError(String reqid, String cmd, ResultStatus resultstatus, String errorMsg) { 00720 HJsonDictionnary obj = new HJsonDictionnary(); 00721 obj.put("errorMsg", errorMsg); 00722 HResult hresult = new HResult(); 00723 hresult.setResult(obj); 00724 hresult.setStatus(resultstatus); 00725 00726 hresult.setReqid(reqid); 00727 hresult.setCmd(cmd); 00728 00729 this.notifyResult(hresult); 00730 } 00731 00737 private void notifyResultError(String reqid, String cmd, ResultStatus resultstatus, String errorMsg, HCommandDelegate commandDelegate) { 00738 HJsonDictionnary obj = new HJsonDictionnary(); 00739 obj.put("errorMsg", errorMsg); 00740 HResult hresult = new HResult(); 00741 hresult.setResult(obj); 00742 hresult.setStatus(resultstatus); 00743 00744 hresult.setReqid(reqid); 00745 hresult.setCmd(cmd); 00746 00747 this.notifyResult(hresult, commandDelegate); 00748 } 00749 00750 00755 private class TransportDelegate implements HTransportDelegate { 00756 00761 public void onStatus(ConnectionStatus status, ConnectionError error, String errorMsg) { 00762 notifyStatus(status, error, errorMsg); 00763 } 00764 00769 public void onData(String type, JSONObject jsonData) { 00770 try { 00771 if(type.equalsIgnoreCase("hresult")) { 00772 notifyResult(new HResult(jsonData)); 00773 } else if (type.equalsIgnoreCase("hmessage")) { 00774 notifyMessage(new HMessage(jsonData)); 00775 } 00776 } catch (Exception e) { 00777 System.out.println("erreur datacallBack"); 00778 } 00779 } 00780 } 00781 00782 00783 }