Package org.eclipse.ecf.internal.provider.xmpp.smack

Source Code of org.eclipse.ecf.internal.provider.xmpp.smack.ECFConnection

/*******************************************************************************
* Copyright (c) 2004 Composent, Inc. and others. All rights reserved. This
* program and the accompanying materials are made available under the terms of
* the Eclipse Public License v1.0 which accompanies this distribution, and is
* available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors: Composent, Inc. - initial API and implementation
******************************************************************************/
package org.eclipse.ecf.internal.provider.xmpp.smack;

import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.eclipse.core.runtime.IAdapterManager;
import org.eclipse.ecf.core.ContainerConnectException;
import org.eclipse.ecf.core.identity.ID;
import org.eclipse.ecf.core.identity.IDFactory;
import org.eclipse.ecf.core.identity.Namespace;
import org.eclipse.ecf.core.security.CallbackHandler;
import org.eclipse.ecf.core.util.ECFException;
import org.eclipse.ecf.internal.provider.xmpp.XmppPlugin;
import org.eclipse.ecf.provider.comm.DisconnectEvent;
import org.eclipse.ecf.provider.comm.IAsynchEventHandler;
import org.eclipse.ecf.provider.comm.IConnectionListener;
import org.eclipse.ecf.provider.comm.ISynchAsynchConnection;
import org.eclipse.ecf.provider.xmpp.identity.XMPPID;
import org.eclipse.ecf.provider.xmpp.identity.XMPPRoomID;
import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.SASLAuthentication;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Bind;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Message.Type;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;

public class ECFConnection implements ISynchAsynchConnection {

  /**
   *
   */
  private static final String GOOGLE_TALK_HOST = "talk.google.com";
  public static final String CLIENT_TYPE = "ecf.";
  public static final boolean DEBUG = Boolean.getBoolean("smack.debug");

  protected static final String STRING_ENCODING = "UTF-8";
  public static final String OBJECT_PROPERTY_NAME = ECFConnection.class
      .getName() + ".object";
  protected static final int XMPP_DEFAULT_PORT = 5222;
  protected static final int XMPPS_DEFAULT_PORT = 5223;

  private XMPPConnection connection = null;
  private IAsynchEventHandler handler = null;
  private boolean isStarted = false;
  private int serverPort = -1;
  private String serverResource;
  private final Map properties = null;
  private boolean isConnected = false;
  private Namespace namespace = null;

  private boolean google = false;

  private boolean disconnecting = false;

  private int BIND_TIMEOUT = new Integer(System.getProperty(
      "org.eclipse.ecf.provider.xmpp.ECFConnection.bindTimeout", "15000"))
      .intValue();

  private Object bindLock = new Object();

  private String jid;

  private CallbackHandler callbackHandler;

  private final PacketListener packetListener = new PacketListener() {
    public void processPacket(Packet arg0) {
      handlePacket(arg0);
    }
  };

  private final ConnectionListener connectionListener = new ConnectionListener() {
    public void connectionClosed() {
      handleConnectionClosed(new IOException("Connection reset by peer"));
    }

    public void connectionClosedOnError(Exception e) {
      handleConnectionClosed(e);
    }

    public void reconnectingIn(int seconds) {
    }

    public void reconnectionFailed(Exception e) {
    }

    public void reconnectionSuccessful() {
    }
  };

  protected void logException(String msg, Throwable t) {
    XmppPlugin.log(msg, t);
  }

  public Map getProperties() {
    return properties;
  }

  /*
   * (non-Javadoc)
   *
   * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
   */
  public Object getAdapter(Class adapter) {
    if (adapter == null)
      return null;
    if (adapter.isInstance(this))
      return this;
    final IAdapterManager adapterManager = XmppPlugin.getDefault()
        .getAdapterManager();
    return (adapterManager == null) ? null : adapterManager.loadAdapter(
        this, adapter.getName());
  }

  public XMPPConnection getXMPPConnection() {
    return connection;
  }

  public ECFConnection(boolean google, Namespace ns, IAsynchEventHandler h) {
    this(google, ns, h, null);
  }

  public ECFConnection(boolean google, Namespace ns, IAsynchEventHandler h,
      CallbackHandler ch) {
    this.handler = h;
    this.namespace = ns;
    this.google = google;
    this.callbackHandler = ch;
    if (DEBUG)
      XMPPConnection.DEBUG_ENABLED = true;
  }

  protected String getPasswordForObject(Object data) {
    String password = null;
    try {
      password = (String) data;
    } catch (final ClassCastException e) {
      return null;
    }
    return password;
  }

  private XMPPID getXMPPID(ID remote) throws ECFException {
    XMPPID jabberID = null;
    try {
      jabberID = (XMPPID) remote;
    } catch (final ClassCastException e) {
      throw new ECFException(e);
    }
    return jabberID;
  }

  public synchronized Object connect(ID remote, Object data, int timeout)
      throws ECFException {
    if (connection != null)
      throw new ECFException("already connected");
    if (timeout > 0)
      SmackConfiguration.setPacketReplyTimeout(timeout);
    Roster.setDefaultSubscriptionMode(Roster.SubscriptionMode.manual);

    final XMPPID jabberURI = getXMPPID(remote);

    String username = jabberURI.getNodename();
    String hostname = jabberURI.getHostname();
    String hostnameOverride = null;

    // Check for the URI form of "joe@bloggs.org;talk.google.com", which
    // would at this point would have
    // - username = "joe"
    // - hostname = "blogs.org;talk.google.com"
    // - hostnameOverride = null
    //
    // We need to turn this into:
    // - username = "joe"
    // - hostname = "bloggs.org"
    // - hostnameOverride = "talk.google.com"

    int semiColonIdx = hostname.lastIndexOf(';');
    if (semiColonIdx != -1) {
      hostnameOverride = hostname.substring(semiColonIdx + 1);
      hostname = hostname.substring(0, semiColonIdx);
    }

    if (google && hostnameOverride == null) {
      hostnameOverride = GOOGLE_TALK_HOST;
    }
    final String serviceName = hostname;

    serverPort = jabberURI.getPort();
    serverResource = jabberURI.getResourceName();
    if (serverResource == null
        || serverResource.equals(XMPPID.PATH_DELIMITER)) {
      serverResource = getClientIdentifier();
      jabberURI.setResourceName(serverResource);
    }
    try {
      ConnectionConfiguration config;
      if (hostnameOverride != null) {
        config = new ConnectionConfiguration(hostnameOverride,
            XMPP_DEFAULT_PORT, serviceName);
      } else if (serverPort == -1) {
        config = new ConnectionConfiguration(serviceName);
      } else {
        config = new ConnectionConfiguration(serviceName, serverPort);
      }
      config.setSendPresence(true);
      // Handler is only used if server requires certificate for
      // authentication; handler should provide keystore password:
      if (callbackHandler instanceof javax.security.auth.callback.CallbackHandler) {
        config.setCallbackHandler((javax.security.auth.callback.CallbackHandler) callbackHandler);
      }
      connection = new XMPPConnection(config);
      connection.connect();

      SASLAuthentication.supportSASLMechanism("PLAIN", 0);

      if (google || GOOGLE_TALK_HOST.equals(hostnameOverride)) {
        username = username + "@" + serviceName;
      }

      connection.addPacketListener(packetListener, null);
      connection.addConnectionListener(connectionListener);

      // Login
      connection.login(username, (String) data, serverResource);

      waitForBindResult();

    } catch (final XMPPException e) {
      throw new ContainerConnectException("Login attempt failed", e);
    }
    return jid;
  }

  private void waitForBindResult() throws XMPPException {
    // We'll wait a maximum of
    long bindTimeout = System.currentTimeMillis() + BIND_TIMEOUT;
    synchronized (bindLock) {
      while (jid == null && System.currentTimeMillis() < bindTimeout) {
        try {
          bindLock.wait(1000);
        } catch (InterruptedException e) {
        }
      }
      if (jid == null)
        throw new XMPPException(
            "timeout waiting for server bind result");
      isConnected = true;
    }
  }

  private String getClientIdentifier() {
    return CLIENT_TYPE + handler.getEventHandlerID().getName();
  }

  public void sendPacket(Packet packet) throws XMPPException {
    if (connection != null)
      connection.sendPacket(packet);
  }

  public synchronized void disconnect() {
    disconnecting = true;
    if (isStarted()) {
      stop();
    }
    if (connection != null) {
      connection.removePacketListener(packetListener);
      connection.removeConnectionListener(connectionListener);
      connection.disconnect();
      connection = null;
      synchronized (bindLock) {
        jid = null;
        isConnected = false;
      }
    }
  }

  public synchronized boolean isConnected() {
    return (isConnected);
  }

  public synchronized ID getLocalID() {
    if (!isConnected())
      return null;
    try {
      return IDFactory.getDefault().createID(namespace.getName(),
          new Object[] { connection.getConnectionID() });
    } catch (final Exception e) {
      logException("Exception in getLocalID", e);
      return null;
    }
  }

  public synchronized void start() {
    if (isStarted())
      return;
    isStarted = true;
  }

  public boolean isStarted() {
    return isStarted;
  }

  public synchronized void stop() {
    isStarted = false;
  }

  protected void handleConnectionClosed(Exception e) {
    if (!disconnecting) {
      disconnecting = true;
      handler.handleDisconnectEvent(new DisconnectEvent(this, e, null));
    }
  }

  protected void handlePacket(Packet arg0) {
    handleJidPacket(arg0);
    try {
      final Object val = arg0.getProperty(OBJECT_PROPERTY_NAME);
      if (val != null) {
        handler.handleAsynchEvent(new ECFConnectionObjectPacketEvent(
            this, arg0, val));
      } else {
        handler.handleAsynchEvent(new ECFConnectionPacketEvent(this,
            arg0));
      }
    } catch (final IOException e) {
      logException("Exception in handleAsynchEvent", e);
      try {
        disconnect();
      } catch (final Exception e1) {
        logException("Exception in disconnect()", e1);
      }
    }
  }

  private void handleJidPacket(Packet packet) {
    if (jid != null)
      return;
    if (packet instanceof IQ) {
      IQ iqPacket = (IQ) packet;
      if (iqPacket.getType().equals(IQ.Type.RESULT)
          && iqPacket instanceof Bind) {
        Bind bindPacket = (Bind) iqPacket;
        synchronized (bindLock) {
          jid = bindPacket.getJid();
          bindLock.notify();
        }
      }
    }
  }

  public synchronized void sendAsynch(ID receiver, byte[] data)
      throws IOException {
    if (data == null)
      throw new IOException("no data");
    final Message aMsg = new Message();
    aMsg.setProperty(OBJECT_PROPERTY_NAME, data);
    sendMessage(receiver, aMsg);
  }

  protected void sendMessage(ID receiver, Message aMsg) throws IOException {
    synchronized (this) {
      if (!isConnected())
        throw new IOException("not connected");
      try {
        if (receiver == null)
          throw new IOException(
              "receiver cannot be null for xmpp instant messaging");
        else if (receiver instanceof XMPPID) {
          final XMPPID rcvr = (XMPPID) receiver;
          aMsg.setType(Message.Type.chat);
          final String receiverName = rcvr.getFQName();
          final Chat localChat = connection.getChatManager()
              .createChat(receiverName, new MessageListener() {
                public void processMessage(Chat chat,
                    Message message) {
                }
              });
          localChat.sendMessage(aMsg);
        } else if (receiver instanceof XMPPRoomID) {
          final XMPPRoomID roomID = (XMPPRoomID) receiver;
          aMsg.setType(Message.Type.groupchat);
          final String to = roomID.getMucString();
          aMsg.setTo(to);
          connection.sendPacket(aMsg);
        } else
          throw new IOException(
              "receiver must be of type XMPPID or XMPPRoomID");
      } catch (final XMPPException e) {
        final IOException result = new IOException(
            "XMPPException in sendMessage: " + e.getMessage());
        result.setStackTrace(e.getStackTrace());
        throw result;
      }
    }
  }

  public synchronized Object sendSynch(ID receiver, byte[] data)
      throws IOException {
    if (data == null)
      throw new IOException("data cannot be null");
    // This is assumed to be disconnect...so we'll just disconnect
    // disconnect();
    return null;
  }

  public void addListener(IConnectionListener listener) {
    // XXX Not yet implemented
  }

  public void removeListener(IConnectionListener listener) {
    // XXX Not yet implemented
  }

  public void sendMessage(ID target, String message) throws IOException {
    if (target == null)
      throw new IOException("target cannot be null");
    if (message == null)
      throw new IOException("message cannot be null");
    final Message aMsg = new Message();
    aMsg.setBody(message);
    sendMessage(target, aMsg);
  }

  public static Map getPropertiesFromPacket(Packet packet) {
    final Map result = new HashMap();
    final Iterator i = packet.getPropertyNames().iterator();
    for (; i.hasNext();) {
      final String name = (String) i.next();
      result.put(name, packet.getProperty(name));
    }
    return result;
  }

  public static Packet setPropertiesInPacket(Packet input, Map properties) {
    if (properties != null) {
      for (final Iterator i = properties.keySet().iterator(); i.hasNext();) {
        final Object keyo = i.next();
        final Object val = properties.get(keyo);
        final String key = (keyo instanceof String) ? (String) keyo
            : keyo.toString();
        input.setProperty(key, val);
      }
    }
    return input;
  }

  public void sendMessage(ID target, ID thread, Type type, String subject,
      String body, Map properties2) throws IOException {
    if (target == null)
      throw new IOException("XMPP target for message cannot be null");
    if (body == null)
      body = "";
    final Message aMsg = new Message();
    aMsg.setBody(body);
    if (thread != null)
      aMsg.setThread(thread.getName());
    if (type != null)
      aMsg.setType(type);
    if (subject != null)
      aMsg.setSubject(subject);
    setPropertiesInPacket(aMsg, properties2);
    sendMessage(target, aMsg);
  }

  public void sendPresenceUpdate(ID target, Presence presence)
      throws IOException {
    if (presence == null)
      throw new IOException("presence cannot be null");
    presence.setFrom(connection.getUser());
    if (target != null)
      presence.setTo(target.getName());
    synchronized (this) {
      if (!isConnected())
        throw new IOException("not connected");
      connection.sendPacket(presence);
    }
  }

  public void sendRosterAdd(String user, String name, String[] groups)
      throws IOException, XMPPException {
    final Roster r = getRoster();
    r.createEntry(user, name, groups);
  }

  public void sendRosterRemove(String user) throws XMPPException, IOException {
    final Roster r = getRoster();
    final RosterEntry re = r.getEntry(user);
    r.removeEntry(re);
  }

  /*
   * (non-Javadoc)
   *
   * @see org.eclipse.ecf.provider.xmpp.IIMMessageSender#getRoster()
   */
  public Roster getRoster() throws IOException {
    if (connection == null || !connection.isConnected())
      return null;
    return connection.getRoster();
  }

}
TOP

Related Classes of org.eclipse.ecf.internal.provider.xmpp.smack.ECFConnection

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.