Hubiquitus Android  0.3
Android client for hubiquitus protocol
HTransportXMPP.java
Go to the documentation of this file.
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.transport.xmpp;
00021 
00022 import java.util.Timer;
00023 import java.util.TimerTask;
00024 
00025 import org.hubiquitus.hapi.hStructures.ConnectionError;
00026 import org.hubiquitus.hapi.hStructures.ConnectionStatus;
00027 import org.hubiquitus.hapi.transport.HTransport;
00028 import org.hubiquitus.hapi.transport.HTransportDelegate;
00029 import org.hubiquitus.hapi.transport.HTransportOptions;
00030 import org.jivesoftware.smack.Connection;
00031 import org.jivesoftware.smack.ConnectionConfiguration;
00032 import org.jivesoftware.smack.ConnectionListener;
00033 import org.jivesoftware.smack.PacketListener;
00034 import org.jivesoftware.smack.SmackConfiguration;
00035 import org.jivesoftware.smack.XMPPConnection;
00036 import org.jivesoftware.smack.filter.FromContainsFilter;
00037 import org.jivesoftware.smack.filter.OrFilter;
00038 import org.jivesoftware.smack.filter.PacketFilter;
00039 import org.jivesoftware.smack.packet.Message;
00040 import org.jivesoftware.smack.packet.Packet;
00041 import org.jivesoftware.smackx.pubsub.EventElement;
00042 import org.jivesoftware.smackx.pubsub.Item;
00043 import org.jivesoftware.smackx.pubsub.ItemsExtension;
00044 import org.jivesoftware.smackx.pubsub.PayloadItem;
00045 import org.json.JSONException;
00046 import org.json.JSONObject;
00047 
00048 
00055 public class HTransportXMPP implements HTransport, ConnectionListener,PacketListener {
00056 
00057          private HTransportDelegate callback = null;
00058          private HTransportOptions options = null;
00059          private Connection connection = null;
00060          private ConnectionConfiguration config = null;
00061          private Thread connectionThread = null;
00062          private ConnectionStatus connectionStatus = ConnectionStatus.DISCONNECTED;
00063          
00064          public HTransportXMPP() {
00065                   //patch for android to add xmpp providers
00066                   SmackConfiguration.setPacketReplyTimeout(10000);
00067                   SmackConfigureProviderManager.configureProviderManager();
00068          };       
00069          
00075          public void connect(HTransportDelegate callback, HTransportOptions options){     
00076                   if (connection != null && connection.isConnected()) {
00077                            connection.disconnect();
00078                   }
00079                   
00080                   this.connectionStatus = ConnectionStatus.CONNECTING;
00081                   
00082                   this.callback = callback;
00083                   this.options = options;
00084                   
00085                   String serverHost = options.getServerHost();
00086                   int serverPort = options.getServerPort();
00087                   String serviceName = options.getJid().getDomain();
00088 
00089                   //each time it connect we create a new configuration and connection objects
00090                   //because smack doesn't allow setting host, or configuration on existing objects
00091                   //@todo check if config has changed rather than create a new one
00092                   this.config = new ConnectionConfiguration(serverHost, serverPort, serviceName);
00093 //                //patch for android to support security
00094                   config.setTruststorePath("/system/etc/security/cacerts.bks");
00095              config.setTruststoreType("bks");
00096 
00097              // Sets whether the client will use SASL authentication when logging into the server.
00098              //config.setSASLAuthenticationEnabled(true);
00099                                    
00100              // Sets the TLS security mode used when making the connection.
00101              //config.setSecurityMode(SecurityMode.required);
00102              
00103              config.setReconnectionAllowed(false);
00104              config.setSendPresence(true);
00105              
00106            //add a timer to monitor connection thread. If it takes too long kill it and return conn timeout
00107              final Timer timer = new Timer();
00108              
00109              try {
00110                   this.connection = new XMPPConnection(config);
00111                  
00112                       final HTransportOptions localOptions = options;
00113                       final HTransportXMPP outerClass = this;
00114                       
00115                       //create a thread to connect async
00116                       this.connectionThread = new Thread(new Runnable() {
00117 
00118                                     public void run() {
00119                                              try {
00120                                                       //launch connection and add connection listener for errors
00121                                                       connection.connect();
00122                                                       connection.addConnectionListener(outerClass);
00123                                                       try {
00124                                                                //try to login and update status
00125                                                                connection.login(localOptions.getUsername(), localOptions.getPassword(), localOptions.getResource());
00126                                                                updateStatus(ConnectionStatus.CONNECTED, ConnectionError.NO_ERROR, null);
00127                                                                PacketFilter hserverPacketFilter = new FromContainsFilter(localOptions.getHserverService());
00128                                                                PacketFilter pubsubPacketFilter = new FromContainsFilter(localOptions.getPubsubService());
00129                                                                PacketFilter packetFilter = new OrFilter(hserverPacketFilter, pubsubPacketFilter);
00130                                                                connection.addPacketListener(outerClass,packetFilter);
00131                                                       } catch(Exception e) { //login failed
00132                                                                boolean wasConnected = false;
00133                                                                if (connection.isConnected()) {
00134                                                                         wasConnected = true;
00135                                                                         connection.disconnect();
00136                                                                }
00137                                                                connection = null;
00138                                                                
00139                                                                //update status only if connected, because if not, it's a network error not a login error
00140                                                                if(wasConnected) {
00141                                                                         updateStatus(ConnectionStatus.DISCONNECTED, ConnectionError.AUTH_FAILED, e.getMessage());
00142                                                                }
00143                                                       }
00144                                                       
00145                                              } catch(Exception e) { //in case connection failed
00146                                                       connection = null;
00147                                                       updateStatus(ConnectionStatus.DISCONNECTED, ConnectionError.TECH_ERROR , e.getMessage());
00148                                              }        
00149                                              timer.cancel();
00150                                     }
00151                            });
00152                       this.connectionThread.start(); //start async thread
00153              } catch(Exception e) { //in case thread creation failed or it was interrupted
00154                   if (connection.isConnected()) {
00155                                     connection.disconnect();
00156                            }
00157                   //e.printStackTrace();
00158                   this.connection = null;
00159                   this.updateStatus(ConnectionStatus.DISCONNECTED, ConnectionError.TECH_ERROR, e.getMessage());
00160              }
00161              
00162              
00163              //set timer task to add a connection timeout
00164              timer.schedule(new TimerTask() {
00165                            
00166                            @Override
00167                            public void run() {
00168                                     if (connectionThread != null && connectionThread.isAlive()) {
00169                                              connectionThread.interrupt();
00170                                              if(connection.isConnected()) {
00171                                                       connection.disconnect();
00172                                              }
00173                                     }
00174                                     updateStatus(ConnectionStatus.DISCONNECTED, ConnectionError.CONN_TIMEOUT, null);
00175                            }
00176                   }, SmackConfiguration.getPacketReplyTimeout());
00177          }
00178          
00185          public void updateStatus(ConnectionStatus status, ConnectionError error, String errorMsg) {
00186                   this.connectionStatus = status;
00187                   if (callback != null) {
00188                            callback.onStatus(status, error, errorMsg);
00189                            if(this.connectionStatus == ConnectionStatus.DISCONNECTED) {
00190                                     callback = null;
00191                            }
00192                   }
00193          }
00194 
00200          public void disconnect() {
00201                   this.connectionStatus = ConnectionStatus.DISCONNECTING;
00202                   
00203                   //stop connection thread if running and to try to disconnect through usual way
00204                   //we should resend disconnected if connection thread is alive
00205                   if(connectionThread != null && connectionThread.isAlive()) {
00206                            connectionThread.interrupt(); 
00207                            if(connection != null && connection.isConnected()) {
00208                                     connection.disconnect();
00209                            }
00210                            updateStatus(ConnectionStatus.DISCONNECTED, null, null);
00211                   } else {
00212                            try {
00213                                //create a thread to disconnect async
00214                                new Thread(new Runnable() {
00215 
00216                                              public void run() {
00217                                                       try {
00218                                                                connection.disconnect();
00219                                                                updateStatus(ConnectionStatus.DISCONNECTED, null, null);
00220                                                       } catch(Exception e) {
00221                                                                connection = null;
00222                                                                updateStatus(ConnectionStatus.DISCONNECTED, ConnectionError.TECH_ERROR, e.getMessage());                    
00223                                                       }
00224                                              }
00225                                     }).start();
00226                       } catch(Exception e) {
00227                            this.connection = null;
00228                            this.updateStatus(ConnectionStatus.DISCONNECTED, ConnectionError.TECH_ERROR, e.getMessage());
00229                       }
00230                   }
00231          }
00232 
00233          @Override
00234          public void sendObject(JSONObject object) {
00235                   if( connectionStatus == ConnectionStatus.CONNECTED) {
00236                            Message msg = new Message(options.getHserverService());
00237                            HMessageXMPP packet = new HMessageXMPP("hcommand",object.toString());
00238                            msg.addExtension(packet);
00239                            connection.sendPacket(msg);
00240                   } else {
00241                            System.out.println("Not connected");
00242                   }                 
00243          }
00244 
00245          @Override
00246          public void processPacket(Packet receivePacket) {
00247                   if(receivePacket.getClass().equals(Message.class)) {
00248                            if(receivePacket.getFrom().matches(options.getHserverService() + ".*")) {
00249                                     HMessageXMPP packetExtention = (HMessageXMPP)receivePacket.getExtension("hbody","");
00250                                     if(packetExtention != null) {
00251                                              JSONObject jsonObj = null;
00252                                              try {
00253                                                       jsonObj = new JSONObject(packetExtention.getContent());
00254                                              } catch (JSONException e) {
00255                                                       e.printStackTrace();
00256                                                       System.out.println("Received malformted JSon object from hserver in :" + this.getClass());
00257                                              }
00258                                              if(jsonObj != null) {
00259                                                       callback.onData(packetExtention.getType(), jsonObj);
00260                                              } else {
00261                                                       System.out.println("Received malformted JSon object from hserver in :" + this.getClass());
00262                                              }
00263                                     }else {
00264                                              System.out.println("erreur lors de la reception : PacketExtension erreur");
00265                                     }
00266                            } else if(receivePacket.getFrom().equalsIgnoreCase(options.getPubsubService())) {
00267                                     Message message = (Message)receivePacket;
00268                                     if(message.getExtension("http://jabber.org/protocol/pubsub#event") instanceof EventElement){
00269 
00270                                              // get the event extension
00271                                              EventElement event = (EventElement)message.getExtension("http://jabber.org/protocol/pubsub#event");
00272 
00273                                              if(event.getEventType().name().equals("items")){ // get the type of the event to check if it actually contains items
00274                                                       // get the event
00275                                                       ItemsExtension extension = (ItemsExtension)event.getEvent();
00276 
00277                                                       // get the items
00278                                                       for(int i = 0; i < extension.getItems().size(); i++){
00279                                                                Item item = (Item)extension.getItems().get(i);
00280                                                                if (item instanceof PayloadItem) {
00281                                                                
00282                                                                         JSONObject jsonObj = null;
00283                                                                         try {
00284                                                                                  @SuppressWarnings("unchecked")
00285                                                                                  PayloadItem<HXMPPPubsubEntry> payloadItem = (PayloadItem<HXMPPPubsubEntry>)item;
00286                                                                                  jsonObj = new JSONObject(payloadItem.getPayload().getContent());
00287                                                                         } catch (JSONException e) {
00288                                                                                  e.printStackTrace();
00289                                                                                  System.out.println("Received malformted JSon object from pubsub in :" + this.getClass());
00290                                                                                  System.err.println("error message :" + e.getMessage());
00291                                                                         }
00292                                                                         if(jsonObj != null) {
00293                                                                                  callback.onData("hmessage", jsonObj);
00294                                                                         } else {
00295                                                                                  System.out.println("Received malformted JSon object from pubsub in :" + this.getClass());
00296                                                                         }
00297                                                                }
00298                                                       }
00299                                              }
00300                                     }
00301                            }
00302                   }
00303          }
00304 
00305          /* Connection listener interface */
00306          @Override
00307          public void connectionClosed() {
00308 
00309          }
00310 
00311          @Override
00312          public void connectionClosedOnError(Exception e) {
00313                   this.connectionThread.interrupt();
00314                   this.connectionThread = null;
00315                   this.connection = null;
00316                   this.updateStatus(ConnectionStatus.DISCONNECTED, ConnectionError.TECH_ERROR, e.getMessage());
00317 
00318          }
00319 
00320          //as we use our reconnection system, this shouldn't be called
00321          @Override
00322          public void reconnectingIn(int arg0) {
00323          }
00324 
00325          //as we use our reconnection system, this shouldn't be called
00326          @Override
00327          public void reconnectionFailed(Exception arg0) {
00328          }
00329 
00330          //as we use our reconnection system, this shouldn't be called
00331          @Override
00332          public void reconnectionSuccessful() {
00333          }
00334 
00335 
00336 }
00337 
 All Classes Namespaces Files Functions Variables