2015-05-05 10 views
16

Ich versuche, das Beispiel here für Smack 4.1.0 anzupassen. und ein wenig verwirrt werden.GCM XMPP Server mit Smack 4.1.0

Speziell ich bin schwer zu verstehen, was die GcmPacketExtension nun erweitern sollte, wie der Konstruktor funktionieren sollte und wie der Provider Provider.addExtensionProvider aktualisiert werden sollte, damit er damit verbunden ist.

Ich bin mir sicher, dass jemand das schon mal gemacht hat, aber ich kann keine Beispiele finden und ich scheine nur mit der Dokumentation im Kreis herumzulaufen.

Jede Hilfe wäre sehr geschätzt, ich bin sicher, die Antwort ist sehr einfach!

aktuellen Code (kompiliert, aber nicht ausgeführt wird):

static { 

    ProviderManager.addExtensionProvider(GCM_ELEMENT_NAME, GCM_NAMESPACE, new ExtensionElementProvider<ExtensionElement>() { 
     @Override 
     public DefaultExtensionElement parse(XmlPullParser parser,int initialDepth) throws org.xmlpull.v1.XmlPullParserException, 
     IOException { 
      String json = parser.nextText(); 
      return new GcmPacketExtension(json); 
     } 
    }); 
} 

und:

private static final class GcmPacketExtension extends DefaultExtensionElement { 

    private final String json; 

    public GcmPacketExtension(String json) { 
     super(GCM_ELEMENT_NAME, GCM_NAMESPACE); 
     this.json = json; 
    } 

    public String getJson() { 
     return json; 
    } 

    @Override 
    public String toXML() { 
     return String.format("<%s xmlns=\"%s\">%s</%s>", 
       GCM_ELEMENT_NAME, GCM_NAMESPACE, 
       StringUtils.escapeForXML(json), GCM_ELEMENT_NAME); 
    } 

    public Stanza toPacket() { 
     Message message = new Message(); 
     message.addExtension(this); 
     return message; 
    } 
} 

Aktuelle Ausnahme:

Exception in thread "main" java.lang.NoClassDefFoundError: de/measite/minidns/DNSCache 
at java.lang.Class.forName0(Native Method) 
at java.lang.Class.forName(Unknown Source) 
at org.jivesoftware.smack.SmackInitialization.loadSmackClass(SmackInitialization.java:213) 
at org.jivesoftware.smack.SmackInitialization.parseClassesToLoad(SmackInitialization.java:193) 
at org.jivesoftware.smack.SmackInitialization.processConfigFile(SmackInitialization.java:163) 
at org.jivesoftware.smack.SmackInitialization.processConfigFile(SmackInitialization.java:148) 
at org.jivesoftware.smack.SmackInitialization.<clinit>(SmackInitialization.java:116) 
at org.jivesoftware.smack.SmackConfiguration.getVersion(SmackConfiguration.java:96) 
at org.jivesoftware.smack.provider.ProviderManager.<clinit>(ProviderManager.java:121) 
at SmackCcsClient.<clinit>(SmackCcsClient.java:58) 
Caused by: java.lang.ClassNotFoundException: de.measite.minidns.DNSCache 
at java.net.URLClassLoader$1.run(Unknown Source) 
at java.net.URLClassLoader$1.run(Unknown Source) 
at java.security.AccessController.doPrivileged(Native Method) 
at java.net.URLClassLoader.findClass(Unknown Source) 
at java.lang.ClassLoader.loadClass(Unknown Source) 
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source) 
at java.lang.ClassLoader.loadClass(Unknown Source) 
... 10 more 
+0

Ich habe es jetzt geschafft, den Code zum Kompilieren und Ausführen zu bekommen. Es sendet erfolgreich Nachrichten, aber ich muss den Empfang noch testen. Ich werde die vollständige Quelle veröffentlichen, sobald alles funktioniert! –

Antwort

27

OK, also ich es geschafft, es zu bekommen nach viel lesen und schmerzen arbeiten, so hier ist eine sehr grobe server-implementierung das Wirklich funktioniert. Offensichtlich nicht für die Produktion und fühle mich frei, alles zu korrigieren, was falsch ist. Ich sage nicht, dass dies der beste Weg ist, aber es funktioniert. Es sendet eine Nachricht und empfängt Nachrichten, zeigt sie jedoch nur im Protokoll an.

import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode; 
 
import org.jivesoftware.smack.ConnectionListener; 
 
import org.jivesoftware.smack.SmackException; 
 
import org.jivesoftware.smack.SmackException.NotConnectedException; 
 
import org.jivesoftware.smack.XMPPConnection; 
 
import org.jivesoftware.smack.XMPPException; 
 
import org.jivesoftware.smack.packet.DefaultExtensionElement; 
 
import org.jivesoftware.smack.packet.Message; 
 
import org.jivesoftware.smack.packet.Stanza; 
 
import org.jivesoftware.smack.filter.StanzaFilter; 
 
import org.jivesoftware.smack.provider.ProviderManager; 
 
import org.jivesoftware.smack.tcp.XMPPTCPConnection; 
 
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration; 
 
import org.jivesoftware.smack.util.StringUtils; 
 
import org.json.simple.JSONValue; 
 
import org.json.simple.parser.ParseException; 
 
import org.xmlpull.v1.XmlPullParser; 
 
import org.jivesoftware.smack.*; 
 
import org.jivesoftware.smack.packet.ExtensionElement; 
 
import org.jivesoftware.smack.provider.ExtensionElementProvider; 
 
import org.jivesoftware.smack.roster.Roster; 
 

 
import java.io.IOException; 
 
import java.util.HashMap; 
 
import java.util.Map; 
 
import java.util.UUID; 
 
import java.util.logging.Level; 
 
import java.util.logging.Logger; 
 

 

 
import javax.net.ssl.SSLSocketFactory; 
 

 
/** 
 
* Sample Smack implementation of a client for GCM Cloud Connection Server. This 
 
* code can be run as a standalone CCS client. 
 
* 
 
* <p>For illustration purposes only. 
 
*/ 
 
public class SmackCcsClient { 
 

 
    private static final Logger logger = Logger.getLogger("SmackCcsClient"); 
 

 
    private static final String GCM_SERVER = "gcm.googleapis.com"; 
 
    private static final int GCM_PORT = 5235; 
 

 
    private static final String GCM_ELEMENT_NAME = "gcm"; 
 
    private static final String GCM_NAMESPACE = "google:mobile:data"; 
 

 
    private static final String YOUR_PROJECT_ID = "<your ID here>"; 
 
    private static final String YOUR_API_KEY = "<your API Key here>"; // your API Key 
 
    private static final String YOUR_PHONE_REG_ID = "<your test phone's registration id here>"; 
 
    
 
    
 
    static { 
 

 
     ProviderManager.addExtensionProvider(GCM_ELEMENT_NAME, GCM_NAMESPACE, new ExtensionElementProvider<ExtensionElement>() { 
 
      @Override 
 
      public DefaultExtensionElement parse(XmlPullParser parser,int initialDepth) throws org.xmlpull.v1.XmlPullParserException, 
 
      IOException { 
 
       String json = parser.nextText(); 
 
       return new GcmPacketExtension(json); 
 
      } 
 
     }); 
 
    } 
 

 
    private XMPPTCPConnection connection; 
 

 
    /** 
 
    * Indicates whether the connection is in draining state, which means that it 
 
    * will not accept any new downstream messages. 
 
    */ 
 
    protected volatile boolean connectionDraining = false; 
 

 
    /** 
 
    * Sends a downstream message to GCM. 
 
    * 
 
    * @return true if the message has been successfully sent. 
 
    */ 
 
    public boolean sendDownstreamMessage(String jsonRequest) throws 
 
      NotConnectedException { 
 
     if (!connectionDraining) { 
 
      send(jsonRequest); 
 
      return true; 
 
     } 
 
     logger.info("Dropping downstream message since the connection is draining"); 
 
     return false; 
 
    } 
 

 
    /** 
 
    * Returns a random message id to uniquely identify a message. 
 
    * 
 
    * <p>Note: This is generated by a pseudo random number generator for 
 
    * illustration purpose, and is not guaranteed to be unique. 
 
    */ 
 
    public String nextMessageId() { 
 
     return "m-" + UUID.randomUUID().toString(); 
 
    } 
 

 
    /** 
 
    * Sends a packet with contents provided. 
 
    */ 
 
    protected void send(String jsonRequest) throws NotConnectedException { 
 
     Stanza request = new GcmPacketExtension(jsonRequest).toPacket(); 
 
     connection.sendStanza(request); 
 
    } 
 

 
    /** 
 
    * Handles an upstream data message from a device application. 
 
    * 
 
    * <p>This sample echo server sends an echo message back to the device. 
 
    * Subclasses should override this method to properly process upstream messages. 
 
    */ 
 
    protected void handleUpstreamMessage(Map<String, Object> jsonObject) { 
 
     // PackageName of the application that sent this message. 
 
     String category = (String) jsonObject.get("category"); 
 
     String from = (String) jsonObject.get("from"); 
 
     @SuppressWarnings("unchecked") 
 
     Map<String, String> payload = (Map<String, String>) jsonObject.get("data"); 
 
     payload.put("ECHO", "Application: " + category); 
 

 
     // Send an ECHO response back 
 
     String echo = createJsonMessage(from, nextMessageId(), payload, 
 
       "echo:CollapseKey", null, false); 
 

 
     try { 
 
      sendDownstreamMessage(echo); 
 
     } catch (NotConnectedException e) { 
 
      logger.log(Level.WARNING, "Not connected anymore, echo message is not sent", e); 
 
     } 
 
     
 
    } 
 

 
    /** 
 
    * Handles an ACK. 
 
    * 
 
    * <p>Logs a INFO message, but subclasses could override it to 
 
    * properly handle ACKs. 
 
    */ 
 
    protected void handleAckReceipt(Map<String, Object> jsonObject) { 
 
     String messageId = (String) jsonObject.get("message_id"); 
 
     String from = (String) jsonObject.get("from"); 
 
     logger.log(Level.INFO, "handleAckReceipt() from: " + from + ",messageId: " + messageId); 
 
    } 
 

 
    /** 
 
    * Handles a NACK. 
 
    * 
 
    * <p>Logs a INFO message, but subclasses could override it to 
 
    * properly handle NACKs. 
 
    */ 
 
    protected void handleNackReceipt(Map<String, Object> jsonObject) { 
 
     String messageId = (String) jsonObject.get("message_id"); 
 
     String from = (String) jsonObject.get("from"); 
 
     logger.log(Level.INFO, "handleNackReceipt() from: " + from + ",messageId: " + messageId); 
 
    } 
 

 
    protected void handleControlMessage(Map<String, Object> jsonObject) { 
 
     logger.log(Level.INFO, "handleControlMessage(): " + jsonObject); 
 
     String controlType = (String) jsonObject.get("control_type"); 
 
     if ("CONNECTION_DRAINING".equals(controlType)) { 
 
      connectionDraining = true; 
 
     } else { 
 
      logger.log(Level.INFO, "Unrecognized control type: %s. This could happen if new features are " + "added to the CCS protocol.", 
 
        controlType); 
 
     } 
 
    } 
 

 
    /** 
 
    * Creates a JSON encoded GCM message. 
 
    * 
 
    * @param to RegistrationId of the target device (Required). 
 
    * @param messageId Unique messageId for which CCS sends an 
 
    *   "ack/nack" (Required). 
 
    * @param payload Message content intended for the application. (Optional). 
 
    * @param collapseKey GCM collapse_key parameter (Optional). 
 
    * @param timeToLive GCM time_to_live parameter (Optional). 
 
    * @param delayWhileIdle GCM delay_while_idle parameter (Optional). 
 
    * @return JSON encoded GCM message. 
 
    */ 
 
    public static String createJsonMessage(String to, String messageId, 
 
      Map<String, String> payload, String collapseKey, Long timeToLive, 
 
      Boolean delayWhileIdle) { 
 
     Map<String, Object> message = new HashMap<String, Object>(); 
 
     message.put("to", to); 
 
     if (collapseKey != null) { 
 
      message.put("collapse_key", collapseKey); 
 
     } 
 
     if (timeToLive != null) { 
 
      message.put("time_to_live", timeToLive); 
 
     } 
 
     if (delayWhileIdle != null && delayWhileIdle) { 
 
      message.put("delay_while_idle", true); 
 
     } 
 
     message.put("message_id", messageId); 
 
     message.put("data", payload); 
 
     return JSONValue.toJSONString(message); 
 
    } 
 

 
    /** 
 
    * Creates a JSON encoded ACK message for an upstream message received 
 
    * from an application. 
 
    * 
 
    * @param to RegistrationId of the device who sent the upstream message. 
 
    * @param messageId messageId of the upstream message to be acknowledged to CCS. 
 
    * @return JSON encoded ack. 
 
    */ 
 
     protected static String createJsonAck(String to, String messageId) { 
 
     Map<String, Object> message = new HashMap<String, Object>(); 
 
     message.put("message_type", "ack"); 
 
     message.put("to", to); 
 
     message.put("message_id", messageId); 
 
     return JSONValue.toJSONString(message); 
 
    } 
 

 
    /** 
 
    * Connects to GCM Cloud Connection Server using the supplied credentials. 
 
    * 
 
    * @param senderId Your GCM project number 
 
    * @param apiKey API Key of your project 
 
    */ 
 
    public void connect(String senderId, String apiKey) 
 
      throws XMPPException, IOException, SmackException { 
 
    \t XMPPTCPConnectionConfiguration config = 
 
    \t \t \t XMPPTCPConnectionConfiguration.builder() 
 
    \t \t \t .setServiceName(GCM_SERVER) 
 
    \t \t  .setHost(GCM_SERVER) 
 
    \t \t  .setCompressionEnabled(false) 
 
    \t \t  .setPort(GCM_PORT) 
 
    \t \t  .setConnectTimeout(30000) 
 
    \t \t  .setSecurityMode(SecurityMode.disabled) 
 
    \t \t  .setSendPresence(false) 
 
    \t \t  .setSocketFactory(SSLSocketFactory.getDefault()) 
 
    \t \t  .build(); 
 
    \t 
 
     connection = new XMPPTCPConnection(config); 
 
     
 
     //disable Roster as I don't think this is supported by GCM 
 
     Roster roster = Roster.getInstanceFor(connection); 
 
     roster.setRosterLoadedAtLogin(false); 
 

 
     logger.info("Connecting..."); 
 
     connection.connect(); 
 

 
     connection.addConnectionListener(new LoggingConnectionListener()); 
 

 
     // Handle incoming packets 
 
     connection.addAsyncStanzaListener(new MyStanzaListener() , new MyStanzaFilter()); 
 

 
     // Log all outgoing packets 
 
     connection.addPacketInterceptor(new MyStanzaInterceptor(), new MyStanzaFilter()); 
 

 
     connection.login(senderId + "@gcm.googleapis.com" , apiKey); 
 
     
 
    } 
 
    
 
    private class MyStanzaFilter implements StanzaFilter 
 
    { 
 
    
 
\t \t \t @Override 
 
\t \t \t public boolean accept(Stanza arg0) { 
 
\t \t \t \t // TODO Auto-generated method stub 
 
\t \t \t \t if(arg0.getClass() == Stanza.class) 
 
\t \t \t \t \t return true; 
 
\t \t \t \t else 
 
\t \t \t \t { 
 
\t \t \t \t \t if(arg0.getTo()!= null) 
 
\t \t \t \t \t \t if(arg0.getTo().startsWith(YOUR_PROJECT_ID)) 
 
\t \t \t \t \t \t \t return true; 
 
\t \t \t \t 
 
\t \t \t \t } 
 
\t \t \t \t 
 
\t \t \t \t return false; 
 
\t \t \t } 
 
    } 
 
    
 
    private class MyStanzaListener implements StanzaListener{ 
 
\t \t 
 
     @Override 
 
     public void processPacket(Stanza packet) { 
 
      logger.log(Level.INFO, "Received: " + packet.toXML()); 
 
      Message incomingMessage = (Message) packet; 
 
      GcmPacketExtension gcmPacket = 
 
        (GcmPacketExtension) incomingMessage. 
 
        getExtension(GCM_NAMESPACE); 
 
      String json = gcmPacket.getJson(); 
 
      try { 
 
       @SuppressWarnings("unchecked") 
 
       Map<String, Object> jsonObject = 
 
         (Map<String, Object>) JSONValue. 
 
         parseWithException(json); 
 

 
       // present for "ack"/"nack", null otherwise 
 
       Object messageType = jsonObject.get("message_type"); 
 

 
       if (messageType == null) { 
 
        // Normal upstream data message 
 
        handleUpstreamMessage(jsonObject); 
 

 
        // Send ACK to CCS 
 
        String messageId = (String) jsonObject.get("message_id"); 
 
        String from = (String) jsonObject.get("from"); 
 
        String ack = createJsonAck(from, messageId); 
 
        send(ack); 
 
       } else if ("ack".equals(messageType.toString())) { 
 
         // Process Ack 
 
         handleAckReceipt(jsonObject); 
 
       } else if ("nack".equals(messageType.toString())) { 
 
         // Process Nack 
 
         handleNackReceipt(jsonObject); 
 
       } else if ("control".equals(messageType.toString())) { 
 
         // Process control message 
 
         handleControlMessage(jsonObject); 
 
       } else { 
 
         logger.log(Level.WARNING, 
 
           "Unrecognized message type (%s)", 
 
           messageType.toString()); 
 
       } 
 
      } catch (ParseException e) { 
 
       logger.log(Level.SEVERE, "Error parsing JSON " + json, e); 
 
      } catch (Exception e) { 
 
       logger.log(Level.SEVERE, "Failed to process packet", e); 
 
      } 
 
     } 
 
    
 
    } 
 
    
 
    private class MyStanzaInterceptor implements StanzaListener 
 
    { 
 
    \t @Override 
 
     public void processPacket(Stanza packet) { 
 
    \t \t logger.log(Level.INFO, "Sent: {0}", packet.toXML()); 
 
    \t } 
 
    \t 
 
    } 
 
    
 

 
    public static void main(String[] args) throws Exception { 
 
     
 
     SmackCcsClient ccsClient = new SmackCcsClient(); 
 

 
     ccsClient.connect(YOUR_PROJECT_ID, YOUR_API_KEY); 
 
     
 
     // Send a sample hello downstream message to a device. 
 
     String messageId = ccsClient.nextMessageId(); 
 
     Map<String, String> payload = new HashMap<String, String>(); 
 
     payload.put("Message", "Ahha, it works!"); 
 
     payload.put("CCS", "Dummy Message"); 
 
     payload.put("EmbeddedMessageId", messageId); 
 
     String collapseKey = "sample"; 
 
     Long timeToLive = 10000L; 
 
     String message = createJsonMessage(YOUR_PHONE_REG_ID, messageId, payload, 
 
       collapseKey, timeToLive, true); 
 

 
     ccsClient.sendDownstreamMessage(message); 
 
     logger.info("Message sent."); 
 
     
 
     //crude loop to keep connection open for receiving messages 
 
     while(true) 
 
     {;} 
 
    } 
 

 
    /** 
 
    * XMPP Packet Extension for GCM Cloud Connection Server. 
 
    */ 
 
    private static final class GcmPacketExtension extends DefaultExtensionElement { 
 

 
     private final String json; 
 

 
     public GcmPacketExtension(String json) { 
 
     \t super(GCM_ELEMENT_NAME, GCM_NAMESPACE); 
 
      this.json = json; 
 
     } 
 

 
     public String getJson() { 
 
      return json; 
 
     } 
 

 
     @Override 
 
     public String toXML() { 
 
      return String.format("<%s xmlns=\"%s\">%s</%s>", 
 
        GCM_ELEMENT_NAME, GCM_NAMESPACE, 
 
        StringUtils.escapeForXML(json), GCM_ELEMENT_NAME); 
 
     } 
 

 
     public Stanza toPacket() { 
 
      Message message = new Message(); 
 
      message.addExtension(this); 
 
      return message; 
 
     } 
 
    } 
 

 
    private static final class LoggingConnectionListener 
 
      implements ConnectionListener { 
 

 
     @Override 
 
     public void connected(XMPPConnection xmppConnection) { 
 
      logger.info("Connected."); 
 
     } 
 
     
 

 
     @Override 
 
     public void reconnectionSuccessful() { 
 
      logger.info("Reconnecting.."); 
 
     } 
 

 
     @Override 
 
     public void reconnectionFailed(Exception e) { 
 
      logger.log(Level.INFO, "Reconnection failed.. ", e); 
 
     } 
 

 
     @Override 
 
     public void reconnectingIn(int seconds) { 
 
      logger.log(Level.INFO, "Reconnecting in %d secs", seconds); 
 
     } 
 

 
     @Override 
 
     public void connectionClosedOnError(Exception e) { 
 
      logger.info("Connection closed on error."); 
 
     } 
 

 
     @Override 
 
     public void connectionClosed() { 
 
      logger.info("Connection closed."); 
 
     } 
 

 
\t \t @Override 
 
\t \t public void authenticated(XMPPConnection arg0, boolean arg1) { 
 
\t \t \t // TODO Auto-generated method stub 
 
\t \t \t 
 
\t \t } 
 
    } 
 
}

ich importiert auch folgende externe JAR-Dateien: (! Könnten sie alle nicht erforderlich, aber die meisten sind)

json-simple-1.1.1.jar

jxmpp-core-0.4.1.jar

jxmpp-util-cache-0.5.0-alpha2.jar

minidns-0.1.3.jar

commons-logging-1.2.jar

Httpclient-4.3.4.jar

xpp3_xpath-1.1.4c.jar

xpp3-1.1 .4c.jar

Für den Kunden habe ich das GCM-Beispielprojekt here verwendet. (Scrollen Sie zum Ende der Seite für den Quelllink)

Hoffe das hilft jemandem!

[23-Oct-2015] Ich bearbeite diese Antwort für andere, die Gradle verwenden ... unten sind alle Abhängigkeiten, die ich benötigt habe, um dies zu kompilieren (fügen Sie das Ende Ihrer build.gradle-Datei hinzu)).

+0

Ich war auf der Suche nach dem gleichen Ding. Danke vielmals. Gespeichert viel Arbeit für mich –

+1

Sie brauchen auch Abhängigkeit in Smack-Java7. Hier sind die Abhängigkeiten für Maven-Projekt. com.googlecode.json-einfach: json-einfach: 1.1.1, org.igniterealtime.smack: Klatschenkleks Java7: 4.1.0, org.igniterealtime.smack: Klaps-tcp: 4.1.0, org .igniterealtime.smack: smack-im: 4.1.0. –

+0

Danke dafür. Was ist die Lizenz dieses Codes? Kann ich es in meinem eigenen Projekt verwenden? – user152468

1

Es gibt zwei Referenzimplementierungen, die von Google für den GCM Cloud Connection Server (XMPP-Endpunkt) bereitgestellt werden.

Beide sind hier:

https://github.com/googlesamples/friendlyping/tree/master/server

Der Java-Server die Smack XMPP-Bibliothek verwendet.

Der Go-Server verwendet Googles eigene Go-GCM-Bibliothek - https://github.com/google/go-gcm

Der Go-Server auch im GCM Spielplatz Beispiel verwendet wird - https://github.com/googlesamples/gcm-playground - so scheint es, wie der Go-Server kann von Google bevorzugt werden. Da es Go ist, kann es ohne Abhängigkeiten bereitgestellt werden, was ein Vorteil gegenüber dem Java-Server ist.

Verwandte Themen