Android Open Source - msf-spaces-sdk-android Connection Handler






From Project

Back to project page msf-spaces-sdk-android.

License

The source code is released under:

Apache License

If you think the Android project msf-spaces-sdk-android listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package de.imc.mirror.sdk.android;
// w ww  .j a v  a2 s.c  o  m
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.Namespace;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.XMLOutputter;
import org.jivesoftware.smack.AccountManager;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smackx.ServiceDiscoveryManager;
import org.jivesoftware.smackx.packet.DiscoverItems;

import de.imc.mirror.sdk.ConnectionConfiguration;
import de.imc.mirror.sdk.ConnectionStatus;
import de.imc.mirror.sdk.ConnectionStatusListener;
import de.imc.mirror.sdk.UserInfo;
import de.imc.mirror.sdk.config.NamespaceConfig;
import de.imc.mirror.sdk.exceptions.ConnectionStatusException;
import de.imc.mirror.sdk.exceptions.EntityExistsException;

/**
 * Java implementation of the connection handler interface.
 * The implementation is based on the XMPPConnection implementation provided by the SMACK library. 
 * @author nicolas.mach(at)im-c.de, simon.schwantzer(at)im-c.de
 */
public class ConnectionHandler implements de.imc.mirror.sdk.ConnectionHandler, ConnectionListener {
  private static final Logger logger = Logger.getLogger(ConnectionHandler.class.getName());
  private XMPPConnection connection;
  private ConnectionConfiguration connectionConfiguration;
  private ConnectionStatus status;
  private List<ConnectionStatusListener> listeners;
  private UserInfo userInfo;
  private String password;
  private Map<String, RequestFuture<Element>> pendingIQRequests;
  private PacketListener iqPacketListener;
  private NetworkInformation networkInformation;

  /**
   * Instantiates a connection handler with the given connection configuration and user credentials.
   * @param user XMPP username of the user to log in when connecting to the server.
   * @param password Password of the user to log in.
   * @param connectionConfiguration Connection configuration.
   */
  public ConnectionHandler(String user, String password, de.imc.mirror.sdk.ConnectionConfiguration connectionConfiguration){
    this.connectionConfiguration = connectionConfiguration;
    this.password = password;
    listeners = new ArrayList<ConnectionStatusListener>();
    setConnectionStatus(ConnectionStatus.OFFLINE);
    ProviderInitializer.initializeProviderManager();
    org.jivesoftware.smack.ConnectionConfiguration config = 
        new org.jivesoftware.smack.ConnectionConfiguration(connectionConfiguration.getHost(),
            connectionConfiguration.getPort());
    SecurityMode mode;
    if (connectionConfiguration.isSecureConnection()){
      mode = SecurityMode.required;
    } else {
      mode = SecurityMode.disabled;
    }
    userInfo = new de.imc.mirror.sdk.android.UserInfo(user, 
                  connectionConfiguration.getDomain(), 
                  connectionConfiguration.getApplicationID());
    config.setSecurityMode(mode);
    config.setSelfSignedCertificateEnabled(connectionConfiguration.isSelfSignedCertificateEnabled());
    config.setReconnectionAllowed(true);
    connection = new XMPPConnection(config);
    
    networkInformation = null;
    pendingIQRequests = new HashMap<String, RequestFuture<Element>>();
    iqPacketListener = new PacketListener() {
      @Override
      public void processPacket(Packet packet) {
        if (pendingIQRequests.containsKey(packet.getPacketID())){
          RequestFuture<Element> iqFuture = pendingIQRequests.get(packet.getPacketID());
          pendingIQRequests.remove(packet.getPacketID());
          SAXBuilder reader = new SAXBuilder();
          Document document = null;
          try {
            document = reader.build(new StringReader(packet.toXML()));
          } catch (Exception e) {
            logger.log(Level.WARNING, "Failed to parse incoming IQ packet.", e);
          }
          Element childElement = document.getRootElement();
          if (childElement != null) {
            iqFuture.setResponse(childElement);
          }
        }
      }
    };
    connection.addPacketListener(iqPacketListener, new PacketTypeFilter(IQ.class));
  }

  /**
   * Returns the connection configuration.
   * @return Current connection configuration. 
   */
  @Override
  public ConnectionConfiguration getConfiguration() {
    return connectionConfiguration;
  }

  /**
   * Returns the connection status.
   * @return Current connection status of this handler.
   */
  @Override
  public ConnectionStatus getStatus() {
    return status;
  }

  /**
   * Adds a listener to be notified when the connection status changes.
   * @param listener Listener to add. 
   */
  @Override
  public void addConnectionStatusListener(ConnectionStatusListener listener) {
    listeners.add(listener);
  }

  /**
   * Removes a connection status listener.
   * If the listener is not set, nothing will happen.
   * @param listener Listener to remove.
   */
  @Override
  public void removeConnectionStatusListener(ConnectionStatusListener listener) {
    listeners.remove(listener);
  }

  /**
   * Tries to establish a connection and perform the login.
   * @throws ConnectionStatusException The connection could not be established.
   */
  @Override
  public void connect() throws ConnectionStatusException {
    this.establishConnection();
    this.login();
  }

  /**
   * Tries to establish a connection, create the configured user, and perform the login.
   * @throws UnsupportedOperationException <code>createUser</code> is set to <code>true</code>, but the XMPP server does not support in-band registration.
   * @throws EntityExistsException <code>createUser</code> is set to <code>true</code>, but a XMPP user account with the given username already exists.
   * @throws ConnectionStatusException The connection could not be established.
   */
  @Override
  public void connectAndCreateUser() throws UnsupportedOperationException, EntityExistsException, ConnectionStatusException {
    this.establishConnection();
    this.createUser();
    this.login();
  }

  private void establishConnection() throws ConnectionStatusException{
    if (status == ConnectionStatus.ONLINE) {
      throw new ConnectionStatusException("A connection is already established.");
    }
    try {
      setConnectionStatus(ConnectionStatus.PENDING);
      connection.connect();
      connection.addConnectionListener(this);
    } catch (XMPPException e) {
      setConnectionStatus(ConnectionStatus.ERROR);
      throw new ConnectionStatusException("Failed to establish connection to the XMPP server.", e);
    }
  }
  
  private void login() throws ConnectionStatusException {
    if (status == ConnectionStatus.ONLINE) {
      throw new ConnectionStatusException("A connection is already established.");
    }
    try {
      connection.login(userInfo.getUsername(), password, userInfo.getResource());
      this.networkInformation = requestNetworkInformation();
    } catch (XMPPException e) {
      setConnectionStatus(ConnectionStatus.ERROR);
      throw new ConnectionStatusException("Failed to login to the XMPP server.", e);
    }
    setConnectionStatus(ConnectionStatus.ONLINE);
  }
  
  /**
   * Request information about the XMPP domain.  
   * @return Network information container.
   */
  private NetworkInformation requestNetworkInformation() {
    // Retrieve JID of spaces service and persistence service.
    String spacesServiceJID = null;
    String persistenceServiceJID = null;
    ServiceDiscoveryManager discoveryManager = ServiceDiscoveryManager.getInstanceFor(connection);
    DiscoverItems items;
    try {
      items = discoveryManager.discoverItems(connectionConfiguration.getDomain());
      if (items.getType() == IQ.Type.RESULT) {
        Iterator<DiscoverItems.Item> itemIterator = items.getItems();
        while (spacesServiceJID == null && itemIterator.hasNext()) {
          DiscoverItems.Item item = itemIterator.next();
          if ("MIRROR Spaces Service".equalsIgnoreCase(item.getName())) {
            spacesServiceJID = item.getEntityID();
          } else if ("MIRROR Persistence Service".equalsIgnoreCase(item.getName())) {
            persistenceServiceJID = item.getEntityID();
          }
        }
      }
    } catch (XMPPException e) {
      logger.log(Level.WARNING, "Failed to discover service addresses.", e);
    }
    
    // Retrieve version of the spaces service.
    String spacesServiceVersion = null;
    if (spacesServiceJID != null) {
      IQ versionRequestIQ = new IQ(){

        @Override
        public String getChildElementXML() {
          Element childElement = new Element("spaces", NamespaceConfig.SPACES_SERVICE);
          childElement.addContent(new Element("version", NamespaceConfig.SPACES_SERVICE));
          XMLOutputter out = new XMLOutputter();
          return out.outputString(childElement);
        }
      };
      versionRequestIQ.setType(IQ.Type.GET);
      versionRequestIQ.setTo(spacesServiceJID);
      versionRequestIQ.setFrom(userInfo.getFullJID());
      
      RequestFuture<Element> iqFuture = new RequestFuture<Element>();
      
      pendingIQRequests.put(versionRequestIQ.getPacketID(), iqFuture);
      connection.sendPacket(versionRequestIQ);
      
      try {
        Element response = iqFuture.get(2000, TimeUnit.MILLISECONDS);
        if (response != null){
          if ("result".equals(response.getAttributeValue("type"))) {
            spacesServiceVersion = response.getChild("spaces", Namespace.getNamespace(NamespaceConfig.SPACES_SERVICE)).getChildText("version", Namespace.getNamespace(NamespaceConfig.SPACES_SERVICE));
          } else {
            logger.log(Level.WARNING, "Received error response on spaces service request.");
          }
        }
      } catch (InterruptedException e) {
        logger.log(Level.WARNING, "Receiving a response was interrupted.", e);
      } catch (ExecutionException e) {
        logger.log(Level.WARNING, "Failed to handle IQ response.", e);
      } catch (TimeoutException e) {
        logger.log(Level.WARNING, "IQ request timed out.", e);
      }
    }
    return new NetworkInformation(spacesServiceJID, spacesServiceVersion, persistenceServiceJID);
  }

  /**
   * Tries to disconnect from the XMPP server.
   */
  @Override
  public void disconnect() {
    if (status == ConnectionStatus.OFFLINE) {
      return;
    }
    setConnectionStatus(ConnectionStatus.PENDING);
    connection.removeConnectionListener(this);
    connection.disconnect();
    setConnectionStatus(ConnectionStatus.OFFLINE);
  }

  /**
   * Returns the user information of the currently logged in user.
   * @return User information.
   */
  @Override
  public UserInfo getCurrentUser() {
    return userInfo;
  }
  
  @Override
  public NetworkInformation getNetworkInformation() {
    return networkInformation;
  }

  /**
   * Returns the XMPPConnection object used by this implementation.
   * @return SMACK XMPPConnection handler. 
   */
  public XMPPConnection getXMPPConnection(){
    return connection;
  }
  
  /**
   * Sets the connection status and notifies all connection status listeners.
   */
  private void setConnectionStatus(ConnectionStatus newStatus) {
    this.status = newStatus;
    for (ConnectionStatusListener listener : listeners){
      listener.connectionStatusChanged(status);
    }
  }

  @Override
  public void connectionClosed() {
    setConnectionStatus(ConnectionStatus.OFFLINE);
  }

  @Override
  public void connectionClosedOnError(Exception e) {
    setConnectionStatus(ConnectionStatus.ERROR);
  }

  @Override
  public void reconnectingIn(int seconds) {
    setConnectionStatus(ConnectionStatus.ERROR);    
  }

  @Override
  public void reconnectionFailed(Exception e) {
    setConnectionStatus(ConnectionStatus.PENDING);
  }

  @Override
  public void reconnectionSuccessful() {
    setConnectionStatus(ConnectionStatus.ONLINE);
  }

  private void createUser() throws UnsupportedOperationException,
      EntityExistsException {
    if (status == ConnectionStatus.ONLINE) {
        throw new EntityExistsException("The user " + userInfo.getUsername() + " already exists.");
    }
    AccountManager manager = connection.getAccountManager();
    if (!manager.supportsAccountCreation()){
      setConnectionStatus(ConnectionStatus.ERROR);
      throw new UnsupportedOperationException("The server doesn't support account creation.");
    }
    try {
      manager.createAccount(userInfo.getUsername(), password);
    } catch (XMPPException e) {
      setConnectionStatus(ConnectionStatus.ERROR);
      switch (e.getXMPPError().getCode()){
      case 409:
        throw new EntityExistsException("The user " + userInfo.getUsername() + " already exists.", e);
      case 406:
        throw new IllegalArgumentException("Not enough or malformed arguments to create user.", e);
      default:
        throw new UnsupportedOperationException("The user couldn't be created.", e);
      }
    }
  }
}




Java Source Code List

de.imc.mirror.sdk.android.CDMDataBuilder.java
de.imc.mirror.sdk.android.CDMData.java
de.imc.mirror.sdk.android.CommandType.java
de.imc.mirror.sdk.android.ConnectionConfigurationBuilder.java
de.imc.mirror.sdk.android.ConnectionConfiguration.java
de.imc.mirror.sdk.android.ConnectionHandler.java
de.imc.mirror.sdk.android.DataHandler.java
de.imc.mirror.sdk.android.DataModel.java
de.imc.mirror.sdk.android.DataObjectBuilder.java
de.imc.mirror.sdk.android.DataObject.java
de.imc.mirror.sdk.android.DataWrapper.java
de.imc.mirror.sdk.android.NetworkInformation.java
de.imc.mirror.sdk.android.OrgaSpace.java
de.imc.mirror.sdk.android.PrivateSpace.java
de.imc.mirror.sdk.android.ProviderInitializer.java
de.imc.mirror.sdk.android.RequestFuture.java
de.imc.mirror.sdk.android.SpaceChannel.java
de.imc.mirror.sdk.android.SpaceConfiguration.java
de.imc.mirror.sdk.android.SpaceHandler.java
de.imc.mirror.sdk.android.SpaceMember.java
de.imc.mirror.sdk.android.Space.java
de.imc.mirror.sdk.android.SpacesProvider.java
de.imc.mirror.sdk.android.TeamSpace.java
de.imc.mirror.sdk.android.UserInfo.java
de.imc.mirror.sdk.android.cdm.CDMData_0_1.java
de.imc.mirror.sdk.android.cdm.CDMData_0_2.java
de.imc.mirror.sdk.android.cdm.CDMData_1_0.java
de.imc.mirror.sdk.android.cdm.CDMData_2_0.java
de.imc.mirror.sdk.android.cdm.CreationInfo.java
de.imc.mirror.sdk.android.cdm.Reference.java
de.imc.mirror.sdk.android.cdm.References.java
de.imc.mirror.sdk.android.cdm.Summary.java
de.imc.mirror.sdk.android.data.ChannelsTable.java
de.imc.mirror.sdk.android.data.DataDB.java
de.imc.mirror.sdk.android.data.DataTable.java
de.imc.mirror.sdk.android.data.MembersTable.java
de.imc.mirror.sdk.android.data.SendTable.java
de.imc.mirror.sdk.android.data.SpacesTable.java
de.imc.mirror.sdk.android.exceptions.InvalidBuildException.java
de.imc.mirror.sdk.android.exceptions.RequestException.java
de.imc.mirror.sdk.android.filter.AndFilter.java
de.imc.mirror.sdk.android.filter.DataModelFilter.java
de.imc.mirror.sdk.android.filter.NamespaceFilter.java
de.imc.mirror.sdk.android.filter.OrFilter.java
de.imc.mirror.sdk.android.filter.PeriodFilter.java
de.imc.mirror.sdk.android.filter.PublisherFilter.java
de.imc.mirror.sdk.android.filter.ReferencesFilter.java
de.imc.mirror.sdk.android.packet.DeleteRequestIQ.java
de.imc.mirror.sdk.android.packet.DeleteResponseIQ.java
de.imc.mirror.sdk.android.packet.PersistenceServiceDeleteProvider.java
de.imc.mirror.sdk.android.packet.PersistenceServiceQueryProvider.java
de.imc.mirror.sdk.android.packet.QueryRequestIQ.java
de.imc.mirror.sdk.android.packet.QueryResponseIQ.java
de.imc.mirror.sdk.android.utils.DatatypeConverter.java
de.imc.mirror.sdk.android.utils.DateToXsdDatetimeFormatter.java
org.apache.xerces.jaxp.datatype.DatatypeFactoryImpl.java
org.apache.xerces.jaxp.datatype.DurationImpl.java
org.apache.xerces.jaxp.datatype.SerializedDuration.java
org.apache.xerces.jaxp.datatype.SerializedXMLGregorianCalendar.java
org.apache.xerces.jaxp.datatype.XMLGregorianCalendarImpl.java
org.apache.xerces.util.DatatypeMessageFormatter.java