如何使用Spring Integration将XMPP消息发送到FCM

时间:2019-01-14 13:53:49

标签: java google-cloud-messaging firebase-cloud-messaging xmpp spring-integration

我对XMPP和Spring Integration完全陌生,并且想向FCM用户发送消息。我为出站消息创建了XML配置,如下所示:

    <?xml version="1.0" encoding="UTF-8"?>
 <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:int="http://www.springframework.org/schema/integration"
           xmlns:int-xmpp="http://www.springframework.org/schema/integration/xmpp"
           ...>
  <context:component-scan base-package="com.avantovr.esysync_backend.webservices.restful.fcm.xmppserver" />
  <int-xmpp:outbound-channel-adapter id="xmppOutboundAdapter" channel="xmppOutboundChannel" xmpp-connection="xmppConnection"/>
  <int:service-activator ref="fcmSender" input-channel="xmppOutbound" />
  <int:logging-channel-adapter id="xmppOutboundChannel" log-full-message="true" />
    </beans>

现在,我想创建一个Java类,其中有一种用于通过XMPP将Downstrwam messagr发送到FCM的方法。请问有任何初学者通过Spring集成向FCM发送和接收xmpp消息的示例吗?

1 个答案:

答案 0 :(得分:0)

这可能不是最佳解决方案,但对我来说足够好。第一次处理XMPP。

除了pom.xml依赖项之外,我没有任何XML配置。

睡眠5秒钟,断开连接后重新连接。 FCM会不时断开XMPP连接以平衡负载。

发回ACK正常,未测试接收ACK和NACK。

要从Java-> fcm->手机发送消息;

@Autowired
private XmppConfig xmppConfig;

xmppConfig.getXmppConnection().sendStanza(generateStanza(messageId,json));
来自https://firebase.google.com/docs/cloud-messaging/server

json应该是这样的

  {
      "to":"REGISTRATION_ID",  // "to" replaces "registration_ids"
      "message_id":"m-1366082849205" // new required field
      "data":
      {
          "hello":"world",
      }
      "time_to_live":"600",
      "delivery_receipt_requested": true/false
  }

快速依赖

    <dependency>
        <groupId>org.igniterealtime.smack</groupId>
        <artifactId>smack-tcp</artifactId>
        <version>4.3.4</version>
    </dependency>
    <dependency>
        <groupId>org.igniterealtime.smack</groupId>
        <artifactId>smack-java7</artifactId>
        <version>4.3.4</version>
    </dependency>
    <dependency>
        <groupId>org.igniterealtime.smack</groupId>
        <artifactId>smack-extensions</artifactId>
        <version>4.3.4</version>
    </dependency>
    <dependency>
        <groupId>org.json</groupId>
        <artifactId>json</artifactId>
        <version>20190722</version>
    </dependency>

application.properties

fcm.sender_id=YOUR_SENDER_ID@fcm.googleapis.com
fcm.server_key=YOUR_SERVER_KEY
fcm.host=fcm-xmpp.googleapis.com
fcm.port=5235

XmppConfig.java

import com.alessoft.utils.Utils;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.roster.Roster;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
import org.jxmpp.stringprep.XmppStringprepException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import javax.net.ssl.SSLSocketFactory;

@Configuration
class XmppConfig implements ConnectionListener {

    @Value("${fcm.sender_id}")
    private String senderId;
    @Value("${fcm.server_key}")
    private String server_key;
    @Value("${fcm.host}")
    private String host;
    @Value("${fcm.port}")
    private int port;

    @Autowired
    private XmppService xmppService;

    private Logger logger = LoggerFactory.getLogger(XmppConfig.class);
    private volatile boolean xmppConnected;
    private XMPPTCPConnection xmppConnection;

    public XMPPTCPConnection getXmppConnection() {
        return xmppConnection;
    }

    @PostConstruct
    public void postConstruct() throws Exception {
        new Thread(() -> prepareXmppConnection()).start();
    }

    public XMPPConnection prepareXmppConnection() {
        XMPPTCPConnectionConfiguration conf = null;
        try {
            conf = XMPPTCPConnectionConfiguration.builder()
                    .setHost(host)
                    .setPort(port)
                    .setSendPresence(false)
                    .setSecurityMode(ConnectionConfiguration.SecurityMode.disabled)
                    .setSocketFactory(SSLSocketFactory.getDefault())
                    .setUsernameAndPassword(senderId, server_key)
                    .setXmppDomain("somedomain.com")
                    .build();
        } catch (XmppStringprepException e) {
            logger.info("prepareXmppConnection error", e);
        }
        xmppConnection = new XMPPTCPConnection(conf);
        xmppConnection.addAsyncStanzaListener(xmppService, xmppService);
        xmppConnection.addConnectionListener(this);
        Roster.getInstanceFor(xmppConnection).setRosterLoadedAtLogin(false);
        establishXmppConnection();
        return xmppConnection;
    }

    private void establishXmppConnection() {
        try {
            xmppConnection.connect();
            xmppConnection.login();
        } catch (Exception e) {
            logger.info("XMPP establishXmppConnection error", e);
        }
    }

    @Override
    public void connectionClosedOnError(Exception e) {
        logger.info("LOST CONNECTION TO FCM XMPP ON ERROR", e);
        Utils.sleep(5000);
        establishXmppConnection();
    }

    @Override
    public void connectionClosed() {
        logger.info("LOST CONNECTION TO FCM XMPP");
        Utils.sleep(5000);
        establishXmppConnection();
    }

    @Override
    public void connected(XMPPConnection connection) {
        logger.info("CONNECTED TO FCM XMPP");
    }

    @Override
    public void authenticated(XMPPConnection connection, boolean resumed) {
        logger.info("AUTHENTICATED TO FCM XMPP");
    }

}

XmppService.java

import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.filter.StanzaFilter;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.json.JSONObject;
import org.json.XML;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class XmppService implements StanzaListener, StanzaFilter {

    private Logger logger = LoggerFactory.getLogger(XmppService.class);
    @Autowired
    private XmppConfig xmppConfig;

    @Override
    public void processStanza(Stanza stanzaXml) throws SmackException.NotConnectedException, InterruptedException, SmackException.NotLoggedInException {
        JSONObject stanza = XML.toJSONObject(stanzaXml.toXML(null).toString());
        if (!stanza.has("message")) return;
        JSONObject message = stanza.getJSONObject("message");

        if (message.has("type")) {
            normalMessage(message);
        } else {
            otherMessage(message);
        }
    }

    private void otherMessage(JSONObject message) {
        JSONObject gcm = message.getJSONObject("gcm");
        String contentString = gcm.getString("content");
        JSONObject content = new JSONObject(contentString);
        if (content.getString("message_type").equals("ack")) {
            logger.info("RECEIVED ACK");
        } else if (content.getString("message_type").equals("nack")) {
            logger.info("RECEIVED NACK: \n" + content.toString(2));
        } else {
            logger.info("RECEIVED UNKNOWN: \n" + content.toString());
        }

    }

    private void normalMessage(JSONObject message) {
        JSONObject gcm = message.getJSONObject("gcm");
        String contentString = gcm.getString("content");
        JSONObject content = new JSONObject(contentString);
        String message_id = content.getString("message_id");
        String from = content.getString("from");
        if (content.has("message_type")) {
            logger.info("NOT FROM DEVICE:\n" + message.toString());
        } else {
            processMessage(content);
            sendAck(message_id, from);
        }
    }

    private void processMessage(JSONObject content) {
        // your own processing
    }


    private void sendAck(String message_id, String regId) {
        try {
            JSONObject json = new JSONObject();
            json.put("to", regId);
            json.put("message_id", message_id);
            json.put("message_type", "ack");
            String messageString = String.format("<message><gcm xmlns=\"google:mobile:data\">%s</gcm></message>", json.toString());
            Stanza stanza = PacketParserUtils.parseStanza(messageString);
            xmppConfig.getXmppConnection().sendStanza(stanza);
        } catch (Exception e) {
            logger.info("fcm sending ack error", e);
        }
    }

    private Stanza generateStanza(String messageId, JSONObject json) {
        String messageString = String.format("<message id=\"%s\"><gcm xmlns=\"google:mobile:data\">%s</gcm></message>", messageId, json.toString());
        try {
            return PacketParserUtils.parseStanza(messageString);
        } catch (Exception e) {
            logger.info("generateStanza error", e);
            return null;
        }
    }

    @Override
    public boolean accept(Stanza stanza) {
        return true;
    }
}