无法使用教程“使用 Smack 实现基于 XMPP 的应用服务器的 Java 示例”

Posted

技术标签:

【中文标题】无法使用教程“使用 Smack 实现基于 XMPP 的应用服务器的 Java 示例”【英文标题】:Could not use Tutorial "Java sample implementing an XMPP-based App Server using the Smack" 【发布时间】:2015-05-19 17:24:55 【问题描述】:

从一开始我就是 Java 开发的新手...我尝试在 NetBeans IDE 上使用以下 Java 代码:

import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.PacketInterceptor;
import org.jivesoftware.smack.PacketListener;
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.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.DefaultPacketExtension;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.provider.PacketExtensionProvider;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.util.StringUtils;
import org.json.simple.JSONValue;
import org.json.simple.parser.ParseException;
import org.xmlpull.v1.XmlPullParser;

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

    static 

        ProviderManager.addExtensionProvider(GCM_ELEMENT_NAME, GCM_NAMESPACE,
            new PacketExtensionProvider() 
                @Override
                public PacketExtension parseExtension(XmlPullParser parser) throws
                        Exception 
                    String json = parser.nextText();
                    return new GcmPacketExtension(json);
                
            );
    

    private XMPPConnection 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 
        Packet request = new GcmPacketExtension(jsonRequest).toPacket();
        connection.sendPacket(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(long senderId, String apiKey)
            throws XMPPException, IOException, SmackException 
        ConnectionConfiguration config =
                new ConnectionConfiguration(GCM_SERVER, GCM_PORT);
        config.setSecurityMode(SecurityMode.enabled);
        config.setReconnectionAllowed(true);
        config.setRosterLoadedAtLogin(false);
        config.setSendPresence(false);
        config.setSocketFactory(SSLSocketFactory.getDefault());

        connection = new XMPPTCPConnection(config);
        connection.connect();

        connection.addConnectionListener(new LoggingConnectionListener());

        // Handle incoming packets
        connection.addPacketListener(new PacketListener() 

            @Override
            public void processPacket(Packet 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);
                
            
        , new PacketTypeFilter(Message.class));

        // Log all outgoing packets
        connection.addPacketInterceptor(new PacketInterceptor() 
            @Override
                public void interceptPacket(Packet packet) 
                    logger.log(Level.INFO, "Sent: 0", packet.toXML());
                
            , new PacketTypeFilter(Message.class));

        connection.login(senderId + "@gcm.googleapis.com", apiKey);
    

    public static void main(String[] args) throws Exception 
        final long senderId = 1234567890L; // your GCM sender id
        final String password = "Your API key";

        SmackCcsClient ccsClient = new SmackCcsClient();

        ccsClient.connect(senderId, password);

        // Send a sample hello downstream message to a device.
        String toRegId = "RegistrationIdOfTheTargetDevice";
        String messageId = ccsClient.nextMessageId();
        Map<String, String> payload = new HashMap<String, String>();
        payload.put("Hello", "World");
        payload.put("CCS", "Dummy Message");
        payload.put("EmbeddedMessageId", messageId);
        String collapseKey = "sample";
        Long timeToLive = 10000L;
        String message = createJsonMessage(toRegId, messageId, payload,
                collapseKey, timeToLive, true);

        ccsClient.sendDownstreamMessage(message);
    

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

        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 Packet 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 authenticated(XMPPConnection xmppConnection) 
            logger.info("Authenticated.");
        

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

我在以下链接中找到的源代码: http://developer.android.com/google/gcm/ccs.html#implement

我已经导入了 Smack 4.1.1 以及 json-simple-1.1.1、xmlpull-1.1.3.1、junit...

问题在于 Smack 库,我遇到了很多错误,即使我已经从中导入了所有 jar 文件......我知道 Smack 库在开发过程中有着悠久的历史......我仍然想了解什么我正在做...在以下链接上找到了可能的解决方案: GCM XMPP Server using Smack 4.1.0

也许我可以使用它,也许不能……有教程吗?也许有一些更好的例子? Smack 上的每个版本都有自己的文档吗?

【问题讨论】:

您是否编辑过 build.gradle 文件? 我正在尝试.. 我是 Android 应用开发的新手,但没有成功 【参考方案1】:

build.gradle(模块:app)

apply plugin: 'com.android.application'

android 
    compileSdkVersion 22
    buildToolsVersion "22.0.1"

    defaultConfig 
        applicationId "com.example.sqltest2"
        minSdkVersion 16
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"
    
    buildTypes 
        release 
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        
    



dependencies 
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:22.1.1'
    compile 'com.google.android.gms:play-services:7.3.0'
    compile "org.igniterealtime.smack:smack-android:4.1.0-rc1"
    compile "org.igniterealtime.smack:smack-tcp:4.1.0-rc1"
    compile "org.igniterealtime.smack:smack-extensions:4.1.0-rc1"
    compile "org.igniterealtime.smack:smack-im:4.1.0-rc1"

【讨论】:

以上是关于无法使用教程“使用 Smack 实现基于 XMPP 的应用服务器的 Java 示例”的主要内容,如果未能解决你的问题,请参考以下文章

无法在 Heroku 教程中使用 Python 启动工头

使用 GPU 无法在 tensorflow 教程中运行词嵌入示例

Azure 数字孪生 API 无法使用教程中所述的 DefaultAzureCredential 身份验证方法

Locallapstore使用教程:解决iOS游戏无法内购的破解插件

solr教程无法创建集合

RestKit iOS 教程 - Xcode 无法识别 RestKit 类方法 - 缺少框架?