package com.rummble.fireeagle; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import org.apache.log4j.Logger; import org.jivesoftware.smack.PacketListener; import org.jivesoftware.smack.SASLAuthentication; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.packet.PacketExtension; import org.jivesoftware.smack.packet.XMPPError; import org.jivesoftware.smack.provider.ProviderManager; import org.xmpp.packet.JID; import com.rummble.fireeagle.location.LocationEvent; import com.rummble.fireeagle.smack.packet.PubSubLocationEvent; import com.rummble.fireeagle.smack.packet.PubSubSubscribeOAuth; import com.rummble.fireeagle.smack.packet.PubSubSubscription; import com.rummble.fireeagle.smack.packet.PubSubSubscriptions; import com.rummble.fireeagle.smack.packet.PubSubSubscriptionsOAuth; import com.rummble.fireeagle.smack.packet.PubSubUnsubscribeOAuth; import com.rummble.fireeagle.smack.packet.PubSubSubscriptions.Subscription; import com.rummble.fireeagle.smack.provider.PubSubLocationEventProvider; import com.rummble.fireeagle.smack.provider.PubSubProvider; public class FireeagleConsumer implements PacketListener { private static Logger logger = Logger.getLogger( FireeagleConsumer.class.getName() ); private String username; private String password; private String resource; private String server; private static String pubsubUserNode; JID client; JID fireeagle; private XMPPConnection conn; private FireeagleConsumerListener listener; // Maps of packetIds to tokens for messages we have sent and are waiting an answer private Map<String,String> pendingSubscribes; private Map<String,String> pendingUnsubscribes; private Map<String,String> pendingSubscriptions; public FireeagleConsumer(String username,String password,String resource,String server,String fireeagleServer) { pubsubUserNode = "/api/0.1/user/"; this.username = username; this.password = password; this.resource = resource; this.server = server; this.fireeagle = new JID(fireeagleServer); // add an IQ provider for the pubsub IQ packets we will handle ProviderManager.getInstance().addIQProvider("pubsub", PubSubProvider.getNS(), new PubSubProvider()); // add a packet extension for the location events that come back from FireEagle ProviderManager.getInstance().addExtensionProvider("event", PubSubLocationEventProvider.getNS(), new PubSubLocationEventProvider()); pendingSubscribes = Collections.synchronizedMap(new HashMap<String,String>()); pendingUnsubscribes = Collections.synchronizedMap(new HashMap<String,String>()); pendingSubscriptions = Collections.synchronizedMap(new HashMap<String,String>()); } public void setListener(FireeagleConsumerListener listener) { this.listener = listener; } /** * * Must be called before a connection is created. * * @param val - debug true/false */ public void setDebug(boolean val) { XMPPConnection.DEBUG_ENABLED = val; } /** * * Connect to XMPP server * * @return - whether we are connected */ public boolean connect() { try { conn=new XMPPConnection(server); conn.connect(); if (conn.isConnected()) { // Required hack for smack 3_1_0 bug // May not be needed for all XMPP servers but doesn't seem to cause harm SASLAuthentication.supportSASLMechanism("PLAIN", 0); conn.login(username, password,resource); client = new JID(conn.getUser()); conn.addPacketListener(this, null); return true; } else { logger.error("Bad connection on createConnection"); return false; } } catch (XMPPException ex) { logger.warn("Error creating connection " + ex.getMessage(),ex); return false; } } /** * Subscribe to node for token * @param consumer * @param token * @param tokenSecret */ public void subscribe(String consumerKey,String consumerSecret,String token,String tokenSecret) { PubSubSubscribeOAuth sub = new PubSubSubscribeOAuth(client,fireeagle); if(sub.createOauthElement(consumerKey,consumerSecret,token,tokenSecret)) { logger.info(sub.toXML()); pendingSubscribes.put(sub.getPacketID(),token); conn.sendPacket(sub); } else logger.error("Failed to create subscription packet for "); } /** * Unsubscribe from node for token * @param consumer * @param token * @param tokenSecret */ public void unsubscribe(String consumerKey,String consumerSecret,String token,String tokenSecret) { PubSubUnsubscribeOAuth sub = new PubSubUnsubscribeOAuth(client,fireeagle); if(sub.createOauthElement(consumerKey,consumerSecret,token,tokenSecret)) { logger.info(sub.toXML()); pendingUnsubscribes.put(sub.getPacketID(),token); conn.sendPacket(sub); } else logger.error("Failed to create unsubscription packet for "); } /** * Get subscriptions * @param consumer * @param token * @param tokenSecret */ public void subscriptions(String consumerKey,String consumerSecret,String token,String tokenSecret) { PubSubSubscriptionsOAuth sub = new PubSubSubscriptionsOAuth(client,fireeagle); if(sub.createOauthElement(consumerKey,consumerSecret,token,tokenSecret)) { logger.info(sub.toXML()); pendingSubscriptions.put(sub.getPacketID(),token); conn.sendPacket(sub); } else logger.error("Failed to create subscription packet for "); } /** * Process a packet from Smack. */ public void processPacket(Packet packet) { logger.info("Received:" + packet.toXML()); if (packet instanceof PubSubSubscription) { pendingSubscribes.remove(packet.getPacketID()); PubSubSubscription subscription = (PubSubSubscription) packet; logger.info("Got subscription packet for " + subscription.getNode()); if (listener != null) listener.subscribeSuccess(subscription.getNode()); } else if (packet instanceof Message) { Message message = (Message) packet; PacketExtension pe = message.getExtension(PubSubLocationEventProvider.getNS()); if (pe != null && pe instanceof PubSubLocationEvent) { LocationEvent locEvent = ((PubSubLocationEvent) pe).getLoc(); if (listener != null) { listener.locationEvent(locEvent); } } } else if (packet instanceof PubSubSubscriptions) { pendingSubscriptions.remove(packet.getPacketID()); PubSubSubscriptions subs = (PubSubSubscriptions) packet; logger.info("Got subscriptions packet"); if (listener != null) { // return tokens to listener HashSet<String> tokens = new HashSet<String>(); for(Subscription sub : subs.getSubscriptions()) { String node = sub.getNode(); String token = node.substring(pubsubUserNode.length()); if (node.startsWith(pubsubUserNode)) tokens.add(token); } listener.subscriptionsSuccess(tokens); } } else { if (packet instanceof IQ) { IQ iq = (IQ) packet; String packetID = packet.getPacketID(); if (pendingSubscribes.containsKey(packetID)) { if(iq.getType() == IQ.Type.ERROR && listener != null) { XMPPError error = packet.getError(); if (error != null) listener.subscribeFailure(pendingSubscribes.get(packetID),error.getCode(), error.getMessage()); else listener.subscribeFailure(pendingSubscribes.get(packetID),-1, "Unknown Error"); } pendingSubscribes.remove(packetID); } else if (pendingUnsubscribes.containsKey(packetID)) { if(iq.getType() == IQ.Type.ERROR && listener != null) { XMPPError error = packet.getError(); if (error != null) listener.unsubscribeFailure(pendingUnsubscribes.get(packetID),error.getCode(), error.getMessage()); else listener.unsubscribeFailure(pendingUnsubscribes.get(packetID),-1, "Unknown Error"); } else if (iq.getType() == IQ.Type.RESULT && listener != null) { listener.unsubscribeSuccess(pendingUnsubscribes.get(packetID)); } pendingUnsubscribes.remove(packetID); } else if (pendingSubscriptions.containsKey(packetID)) { if(iq.getType() == IQ.Type.ERROR && listener != null) { XMPPError error = packet.getError(); if (error != null) listener.subscriptionsFailure(pendingSubscriptions.get(packetID),error.getCode(), error.getMessage()); else listener.subscriptionsFailure(pendingSubscriptions.get(packetID),-1, "Unknown Error"); } pendingSubscriptions.remove(packetID); } } } } public static String getPubsubUserNode() { return pubsubUserNode; } public static void setPubsubUserNode(String node) { pubsubUserNode = node; } }