首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >使用Smack 4.1.0的GCM XMPP服务器

使用Smack 4.1.0的GCM XMPP服务器
EN

Stack Overflow用户
提问于 2015-05-05 23:15:50
回答 2查看 8.1K关注 0票数 16

我正在尝试将示例提供的here改编为Smack 4.1.0。变得有点困惑。

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

我确信以前一定有人这样做过,但我找不到任何示例,而且我似乎只是在使用文档绕圈子。

任何帮助都将不胜感激,我相信答案很简单!

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

代码语言:javascript
复制
    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);
        }
    });
}

和:

代码语言:javascript
复制
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;
    }
}

当前异常:

代码语言:javascript
复制
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
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-05-07 14:51:18

好的,在经历了大量的阅读和痛苦之后,我设法让它工作了,所以这是一个非常粗糙的服务器实现,它实际上是工作的。显然不是用来生产的,你可以随时纠正任何错误的地方。我不是说这是最好的方法,但它确实有效。它将发送消息和接收消息,但只在日志中显示它们。

代码语言:javascript
复制
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
			
		}
    }
}

我还导入了以下外部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。(滚动到页面底部的源链接)

希望这对某些人有帮助!

2015年10月23日我正在为其他使用Gradle的人编辑这个答案...下面是我编译它所需要的所有依赖项(添加到build.gradle文件的底部)。

代码语言:javascript
复制
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'
}
票数 27
EN

Stack Overflow用户

发布于 2016-04-23 10:19:22

Google为GCM Cloud Connection Server (XMPP端点)提供了两个参考实现。

两个都在这里:

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

Java服务器使用Smack XMPP库。

Go服务器使用谷歌自己的go-gcm库- https://github.com/google/go-gcm

Go服务器也被用在GCM Playground的例子中-- https://github.com/googlesamples/gcm-playground --所以Go服务器似乎是谷歌的首选。作为Go,它可以在没有任何依赖的情况下进行部署,这是相对于Java服务器的一个优势。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/30056939

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档