使用Smack 4.1.0的GCM XMPP服务器 [英] GCM XMPP Server using Smack 4.1.0

查看:176
本文介绍了使用Smack 4.1.0的GCM XMPP服务器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试改编为Smack 4.1提供的此处的示例。 0。并有点困惑。



具体来说,我很努力地理解GcmPacketExtension现在应该扩展什么,构造函数应该如何工作以及如何更新Providermanager.addExtensionProvider以配合它。 / p>

我肯定有人必须这样做,但我找不到任何示例
,而且我似乎只是使用文档绕圈子。



任何帮助都会得到很大的赞赏,我相信答案很简单!



当前代码正在编译但未运行):

 静态{

ProviderManager.addExtensionProvider(GCM_ELEMENT_NAME ,GCM_NAMESPACE,新ExtensionElementProvider< ExtensionElement>(){
@覆盖
公共DefaultExtensionElement解析(XmlPullParser解析器,整数initialDepth)抛出org.xmlpull.v1.XmlPullParserException,
IOException的{
字符串json = parser.nextText();
返回新的GcmPacketExtension(json);
}
});

$ / code>

和:

  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);
返回消息;


$ / code $ / pre
$ b当前例外

 线程main中的异常java.lang.NoClassDefFoundError:de / measite / minidns / DNSCache $ b $ java.lang.Class.forName0 )
在java.lang.Class.forName(未知源)
在org.jivesoftware.smack.SmackInitialization.loadSmackClass(SmackInitialization.java:213)
在org.jivesoftware.smack.SmackInitialization .parseClassesToLoad(SmackInitialization.java:193)
在org.jivesoftware.smack.SmackInitialization.processConfigFile(SmackInitialization.java:163)
在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)
在org.jivesoftware.smack.provider.ProviderManager。< clinit>(ProviderManager.java:121)
在SmackCcsClient。< clinit>(Smack CcsClient.java:58)
导致:java.lang.ClassNotFoundException:de.measite.minidns.DNSCache在java.net.URLClassLoader
$ 1.run(未知源)$ b $在java。 net.URLClassLoader $ 1.run(未知源)
at java.security.AccessController.doPrivileged(Native Method)$ b $ java.net.URLClassLoader.findClass(Unknown Source)
at java.lang .ClassLoader.loadClass(未知源)
在sun.misc.Launcher $ AppClassLoader.loadClass(未知源)$ b $在java.lang.ClassLoader.loadClass(未知源)
... 10好的,所以我设法让它在工作之后工作。许多阅读和痛苦,所以这里是一个非常原始的服务器实现,实际上工作。显然不是为了生产,而是随意纠正任何错误的东西。我不是说这是做到这一点的最好方式,但它确实有效。它会发送一条消息并接收消息,但只会在日志中显示它们。



  import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode; import org .jivesoftware.smack.ConnectionListener;进口org.jivesoftware.smack.SmackException;进口org.jivesoftware.smack.SmackException.NotConnectedException;进口org.jivesoftware.smack.XMPPConnection;进口org.jivesoftware.smack.XMPPException;进口org.jivesoftware。 smack.packet.DefaultExtensionElement;进口org.jivesoftware.smack.packet.Message;进口org.jivesoftware.smack.packet.Stanza;进口org.jivesoftware.smack.filter.StanzaFilter;进口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.JSO NValue; 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; / ** * GCM Cloud Connection Server的客户端示例Smack实现。该*代码可以作为独立的CCS客户端运行。 * *< p>仅用于说明目的。 * / public class SmackCcsClient {private static final Logger logger = Logger.getLogger(SmackCcsClient);私人静态最终字符串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 =<你的ID在这里>; private static final String YOUR_API_KEY =<你的API密钥在这里>; //你的API密钥private static final String YOUR_PHONE_REG_ID =<你的测试手机的注册ID在这里>;静态{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();返回新的GcmPacketExtension(json);}}); }私有XMPPTCPConnection连接; / ** *指示连接是否处于排空状态,这意味着它*不会接受任何新的下游消息。 * / protected volatile boolean connectionDraining = false; / ** *发送一个下游消息给GCM。 * * @如果消息已成功发送,则返回true。 * / public boolean sendDownstreamMessage(String jsonRequest)throws NotConnectedException {if(!connectionDraining){send(jsonRequest);返回true; } logger.info(由于连接正在耗尽而丢弃下游消息);返回false; } / ** *返回一个随机消息ID来唯一标识一条消息。 * *< p>注意:这是由一个伪随机数发生器产生的*说明目的,并不保证是唯一的。 * / public String nextMessageId(){returnm-+ UUID.randomUUID()。toString(); } / ** *发送一个包含提供的内容。 * / protected void send(String jsonRequest)throws NotConnectedException {Stanza request = new GcmPacketExtension(jsonRequest).toPacket(); connection.sendStanza(请求); } / ** *处理来自设备应用程序的上行数据消息。 * *< p>此示例回显服务器将回显消息发送回设备。 *子类应该重写此方法以正确处理上游消息。 * / protected void handleUpstreamMessage(Map< String,Object> jsonObject){//发送此消息的应用程序的PackageName。 String category =(String)jsonObject.get(category);来自=(String)的字符串jsonObject.get(from); @SuppressWarnings(unchecked)Map< String,String> payload =(Map< String,String>)jsonObject.get(data); payload.put(ECHO,应用程序:+类别); //发送ECHO响应返回String echo = createJsonMessage(from,nextMessageId(),payload,echo:CollapseKey,null,false);尝试{sendDownstreamMessage(echo); } catch(NotConnectedException e){logger.log(Level.WARNING,不再连接,不发送回显消息,e); }} / ** *处理一个ACK。 * *< p>记录INFO消息,但是子类可以覆盖它以正确处理ACK。 * / protected void handleAckReceipt(Map< String,Object> jsonObject){String messageId =(String)jsonObject.get(message_id);来自=(String)的字符串jsonObject.get(from); logger.log(Level.INFO,handleAckReceipt()from:+ from +,messageId:+ messageId); } / ** *处理一个NACK。 * *< p>记录INFO消息,但是子类可以覆盖它以正确处理NACK。 * / protected void handleNackReceipt(Map< String,Object> jsonObject){String messageId =(String)jsonObject.get(message_id);来自=(String)的字符串jsonObject.get(from); logger.log(Level.INFO,handleNackReceipt()from:+ from +,messageId:+ messageId); });}}}}}}}保护无效handleControlMessage(地图< String,对象> 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,无法识别的控制类型:%s。如果新功能被添加到CCS协议中,可能会发生这种情况。,controlType); }} / ** *创建一个JSON编码的GCM消息。 * * @param到目标设备的RegistrationId(必需)。 * @param messageId CCS发送*ack / nack的唯一messageId(必需)。 * @param payload用于应用程序的消息内容。 (可选的)。 * @param collapseKey GCM collapse_key参数(可选)。 * @param timeToLive GCM time_to_live参数(可选)。 * @param delayWhileIdle GCM delay_while_idle参数(可选)。 * @返回JSON编码的GCM消息。 * / 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);返回JSONValue.toJSONString(message); } / ** *为从应用程序收到*的上游消息创建一个JSON编码的ACK消息。 * * @param发送给发送上行消息的设备的RegistrationId。 * @param messageId要向CCS确认的上游消息的messageId。 * @返回JSON编码的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);返回JSONValue.toJSONString(message); } / ** *使用提供的凭证连接到GCM云连接服务器。 * * @参数senderId您的GCM项目数* * /公共无效连接(字符串senderId,字符串apiKey)项目的@参数apiKey API密钥抛出XMPPException,IOException异常,SmackException {XMPPTCPConnectionConfiguration配置= XMPPTCPConnectionConfiguration.builder().setServiceName(GCM_SERVER) .setHost(GCM_SERVER).setCompressionEnabled(假).setPort(GCM_PORT).setConnectTimeout(30000).setSecurityMode(SecurityMode.disabled).setSendPresence(假).setSocketFactory(SSLSocketFactory.getDefault()).build(); connection = new XMPPTCPConnection(config); //停用Roster,因为我不认为这是GCM Roster支持的roster = Roster.getInstanceFor(connection); roster.setRosterLoadedAtLogin(假); logger.info( 连接...); connection.connect(); connection.addConnectionListener(new LoggingConnectionListener()); //处理传入的数据包connection.addAsyncStanzaListener(new MyStanzaListener(),new MyStanzaFilter()); //记录所有传出的数据包connection.addPacketInterceptor(new MyStanzaInterceptor(),new MyStanzaFilter()); connection.login(senderId +@ gcm.googleapis.com,apiKey); }私人类MyStanzaFilter实现StanzaFilter {@Override public boolean accept(Stanza arg0){// TODO自动生成的方法存根if(arg0.getClass()== Stanza.class)返回true; else {if(arg0.getTo()!= null)if(arg0.getTo()。startsWith(YOUR_PROJECT_ID))返回true; }返回false; }} private class MyStanzaListener implements StanzaListener {@Override public void processPacket(Stanza packet){logger.log(Level.INFO,Received:+ packet.toXML());消息incomingMessage =(消息)数据包; GcmPacketExtension gcmPacket =(GcmPacketExtension)incomingMessage。 getExtension(GCM_NAMESPACE); String json = gcmPacket.getJson();尝试{@SuppressWarnings(unchecked)Map< String,Object> jsonObject =(Map< String,Object>)JSONValue。 parseWithException(JSON); //呈现为ack/nack,否则为null Object messageType = jsonObject.get(message_type); if(messageType == null){//正常上游数据消息handleUpstreamMessage(jsonObject); //发送ACK到CCS字符串messageId =(String)jsonObject.get(message_id);来自=(String)的字符串jsonObject.get(from); String ack = createJsonAck(from,messageId);发送(ACK); } else if(ack.equals(messageType.toString())){// Process Ack handleAckReceipt(jsonObject); } else if(nack.equals(messageType.toString())){//处理Nack handleNackReceipt(jsonObject); } else if(control.equals(messageType.toString())){//过程控制消息handleControlMessage(jsonObject); }其他{logger.log(Level.WARNING,无法识别的消息类型(%s),messageType.toString()); }} catch(ParseException e){logger.log(Level.SEVERE,Error parsing JSON+ json,e); } catch(Exception e){logger.log(Level.SEVERE,无法处理数据包,e); }}} private class MyStanzaInterceptor implements StanzaListener {@Override public void processPacket(Stanza packet){logger.log(Level.INFO,Sent:{0},packet.toXML()); }} public static void main(String [] args)throws Exception {SmackCcsClient ccsClient = new SmackCcsClient();} ccsClient.connect(YOUR_PROJECT_ID,YOUR_API_KEY); //向设备发送样本hello下游消息。 String messageId = ccsClient.nextMessageId();地图< String,String> payload = new HashMap< String,String>(); payload.put(Message,Ahha,It works!); payload.put(CCS,虚拟消息); payload.put(EmbeddedMessageId,messageId); String collapseKey =sample; Long timeToLive = 10000L; String message = createJsonMessage(YOUR_PHONE_REG_ID,messageId,payload,collapseKey,timeToLive,true); ccsClient.sendDownstreamMessage(消息); logger.info(Message sent。); //粗循环保持连接打开以接收消息while(true){;}} / ** * GCM云连接服务器的XMPP数据包扩展。 * / 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(本);返回消息; }} private static final class LoggingConnectionListener实现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,在%d secs中重新连接,秒); } @Override public void connectionClosedOnError(Exception e){logger.info(Connection on error。); } @Override public void connectionClosed(){logger.info(Connection closed。); } @Override public void authenticated(XMPPConnection arg0,boolean arg1){// TODO Auto-generated method stub}}}  



我还导入了以下外部JAR:
(它们可能并非全部都是必需的,但大多数都是!)

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

对于我使用GCM示例项目的客户端here 。 (滚动到页面底部的源代码链接)



希望这有助于某人!



[23-Oct-2015]我正在编辑这个答案,对于那些正在使用Gradle的人...下面是我需要编译的所有依赖(添加到build.gradle文件的底部)。

 依赖关系{
compile'c​​om.googlecode.json -simple:json-simple:1.1.1'
compile'org.igniterealtime.smack:smack-java7:4.1.4'
compile'org.igniterealtime.smack:smack-tcp:4.1.4 '
compile'org.igniterealtime.smack:smack -im:4.1.4'
compile'org.jxmpp:jxmpp-core:0.5.0-alpha6'
compile'org.jxmpp :jxmpp-util-cache:0.5.0-alpha6'
}


I'm trying to adapt the example provided here for Smack 4.1.0. and getting a little confused.

Specifically I'm struggling to understand what the GcmPacketExtension should now extend, how the constructor should work and how the Providermanager.addExtensionProvider should be updated to tie in with it.

I'm sure someone must have done this before but I can't find any examples and I seem to be going round in circles using just the documentation.

Any help would be much appreciated, I'm sure the answer is very simple!

Current Code (is compiling but not running):

    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);
        }
    });
}

and:

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;
    }
}

Current exception:

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

解决方案

OK, so I managed to get it working after much reading and pain, so here is a VERY crude server implementation that actually works. Obviously not for production and feel free to correct anything that's wrong. I'm not saying this is the best way to do it but it does work. It will send a message and receive messages but only shows them in the log.

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 {
    	XMPPTCPConnectionConfiguration config =
    			XMPPTCPConnectionConfiguration.builder()
    			.setServiceName(GCM_SERVER)
    		     .setHost(GCM_SERVER)
    		     .setCompressionEnabled(false)
    		     .setPort(GCM_PORT)
    		     .setConnectTimeout(30000)
    		     .setSecurityMode(SecurityMode.disabled)
    		     .setSendPresence(false)
    		     .setSocketFactory(SSLSocketFactory.getDefault())
    		    .build();
    	
        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
    {
    
			@Override
			public boolean accept(Stanza arg0) {
				// TODO Auto-generated method stub
				if(arg0.getClass() == Stanza.class )
					return true;
				else 
				{
					if(arg0.getTo()!= null)
						if(arg0.getTo().startsWith(YOUR_PROJECT_ID) )
							return true;
				
				}
				
				return false;
			}
    }
    
    private class MyStanzaListener implements StanzaListener{
		
        @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
    {
    	@Override
        public void processPacket(Stanza packet) {
    		logger.log(Level.INFO, "Sent: {0}", packet.toXML());
    	}
    	
    }
    

    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) {
        	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.");
        }

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

I also imported the following external JARs: (they might not all be required but most are!)

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

For the Client I used the GCM sample project here. (Scroll to the bottom of the page for the source link)

Hope this helps someone!

[23-Oct-2015] I'm editing this answer for others who are using Gradle ... below is all the dependencies that I needed to get this to compile (add to the bottom of your build.gradle file).

dependencies {
    compile 'com.googlecode.json-simple:json-simple:1.1.1'
    compile 'org.igniterealtime.smack:smack-java7:4.1.4'
    compile 'org.igniterealtime.smack:smack-tcp:4.1.4'
    compile 'org.igniterealtime.smack:smack-im:4.1.4'
    compile 'org.jxmpp:jxmpp-core:0.5.0-alpha6'
    compile 'org.jxmpp:jxmpp-util-cache:0.5.0-alpha6'
}

这篇关于使用Smack 4.1.0的GCM XMPP服务器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆