Firebase Cloud Messaging – XMPP Server example to receive Upstream Messages | Spring Integration

In the article Firebase Cloud Messaging – How to Send Upstream Messages, we have created an Android App that can send Upstream Messages. Today, we’re gonna look at way to create a Spring Boot Application Server that can receive those Upstream Messages with Spring Integration.

Related Posts:
Firebase Cloud Messaging – How to Send Upstream Messages | Android
How to start Spring Integration with Spring Boot

I. Project Overview

1. Architecture

fcm-xmpp-server-upstream-message-spring-integration-architecture

2. Send Upstream Messages

To know way to create an Android App Client that can send Upstream Messages, please read this post:
Firebase Cloud Messaging – How to Send Upstream Messages | Android

3. Receive Upstream Messages

Spring Integration provides XMPP adapters which support receiving XMPP chat messages:

3.1 Configure the XMPP namespace

– Include in the headers of your XML configuration file:

xmlns:int-xmpp="http://www.springframework.org/schema/integration/xmpp"
xsi:schemaLocation="http://www.springframework.org/schema/integration/xmpp
	http://www.springframework.org/schema/integration/xmpp/spring-integration-xmpp.xsd"
3.2 Establish XMPP connection

But to deal with Firebase Cloud Messaging, we must meet two important requirements:
– initiate a Transport Layer Security (TLS) connection.
– a SASL PLAIN authentication using Sender_Id@gcm.googleapis.com and Server key as the password.

For that complexity, we should use Spring JavaConfig-style of the XMPP Connection configuration:

@Configuration
public class AppConfiguration {

	@Bean
	public XmppConnectionFactoryBean xmppConnectionFactoryBean() {

		XMPPTCPConnectionConfiguration connectionConfiguration = XMPPTCPConnectionConfiguration.builder()
				.setServiceName("grokonez.com")
				.setHost("fcm-xmpp.googleapis.com").setPort(5235)
				.setUsernameAndPassword("xxxxxx@gcm.googleapis.com","xxxxxxxxxxxxxxxxx")
				.setSecurityMode(SecurityMode.ifpossible)
				.setSendPresence(false)
				.setSocketFactory(SSLSocketFactory.getDefault())
				.build();
		
		XmppConnectionFactoryBean connectionFactoryBean = new XmppConnectionFactoryBean();
		connectionFactoryBean.setConnectionConfiguration(connectionConfiguration);
		connectionFactoryBean.setSubscriptionMode(null);

		return connectionFactoryBean;
	}
}
3.3 Inbound Message Channel Adapter





payload-expression is used for the ChatMessageListeningEndpoint.
For the GCM protocol (to work with FCM), we extract the body using expression:

payload-expression="getExtension('google:mobile:data').json"
3.4 Handler for service-activator
@Component("fcmReceiver")
public class FCMReceiver {

	public void get(Message message) {
		//...
	}
}

Everytime an upstream message is received, get() method will be invoked with the help of GcmExtensionProvider for XMPP Extension.

3.5 XMPP Extension

With XMPP messaging extension is FCM, Smack provides the particular org.jivesoftware.smackx.gcm.provider.GcmExtensionProvider and registers that by default with the smack-experimental jar in the classpath.

To import that jar file, add dependency:


	org.igniterealtime.smack
	smack-experimental
	4.1.9

4. Technology

– Java 1.8
– Maven 3.3.9
– Spring Tool Suite – Version 3.8.4.RELEASE
– Spring Boot: 2.0.0.M2

5. Structure

fcm-xmpp-server-upstream-message-spring-integration-structure

integration.xml includes header for Spring Integration and defines bean for Channel, Adapter and Service Activator.
AppConfiguration is @Configuration Component that defines XmppConnectionFactoryBean Bean for inbound-channel-adapter XMPP Connection in integration.xml.
FCMReceiver is handler class with get method for Service Activator.
– Dependencies for Spring Boot Web, Integration, XMPP, Smack in pom.xml

II. Practice

1. Get Sender Id and Server Key

Go to Settings of your Firebase Project in Firebase Console.

2. Create Spring Boot project

– Using Spring Tool Suite/Eclipse to create Spring Boot project.
– Add Dependencies to pom.xml file:


	org.springframework.boot
	spring-boot-starter-web



	org.springframework.boot
	spring-boot-starter-integration



	org.springframework.integration
	spring-integration-xmpp



	org.igniterealtime.smack
	smack-experimental
	4.1.9

3. Define Beans for Channel, Adapter and Service Activator



	

	

	

	

4. Define Bean for XMPP Connection
package com.javasampleapproach.fcm.xmppserver.config;

import javax.net.ssl.SSLSocketFactory;

import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.integration.xmpp.config.XmppConnectionFactoryBean;

@Configuration
@ImportResource("classpath:integration.xml")
public class AppConfiguration {

	@Bean("xmppConnection")
	public XmppConnectionFactoryBean xmppConnectionFactoryBean() {

		XMPPTCPConnectionConfiguration connectionConfiguration = XMPPTCPConnectionConfiguration.builder()
				.setServiceName("grokonez.com")
				.setHost("fcm-xmpp.googleapis.com").setPort(5235)
				.setUsernameAndPassword("xxxxxx@gcm.googleapis.com","xxxxxxxxxxxx")
				.setSecurityMode(SecurityMode.ifpossible)
				.setSendPresence(false)
				.setSocketFactory(SSLSocketFactory.getDefault())
				.build();
		
		XmppConnectionFactoryBean connectionFactoryBean = new XmppConnectionFactoryBean();
		connectionFactoryBean.setConnectionConfiguration(connectionConfiguration);
		connectionFactoryBean.setSubscriptionMode(null);

		return connectionFactoryBean;
	}
}

The subscription-mode initiates the Roster listener to deal with incoming subscriptions from other users. FCM fully disables it. So we should configure it with:

connectionFactoryBean.setSubscriptionMode(null);
5. Handler Class for Service Activator
package com.javasampleapproach.fcm.xmppserver.message;

import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.stereotype.Component;

@Component("fcmReceiver")
public class FCMReceiver {

	public void get(Message message) {

		MessageHeaders headers = message.getHeaders();

		System.out.println("From: " + headers.get("xmpp_from"));
		System.out.println("To: " + headers.get("xmpp_to"));
		System.out.println("Timestamp: " + headers.getTimestamp());
		System.out.println("Payload: " + message.getPayload());
	}
}
6. Run & Check Result

– Config maven build:
clean install
– Run project with mode Spring Boot App.

If the console show the Exception:

org.jivesoftware.smack.roster.Roster     : Exception reloading roster
org.jivesoftware.smack.SmackException$NoResponseException: No response received within reply timeout. Timeout was 5000ms (~5s)...
...

It is just a Smack issue, don’t mind. It doesn’t block incoming messages. Everything work well after this.

– Send upstream Messages from Android App Client:
Open Demo App in Tutorial: Firebase Cloud Messaging – How to Send Upstream Messages | Android to send message:
firebase-cloud-messaging-upstream-messages-ui

– Result in Console Window:

From: devices@gcm.googleapis.com
To: xxxxxx@gcm.googleapis.com
Timestamp: 1499919174220
Payload:
{
   "data": {
      "Key 1": "Value 1",
      "Key 2": "Value 2"
   },
   "time_to_live": 86400,
   "from": "e20_xxxxxxx:XXXXXXXXX",
   "message_id": "1127",
   "category": "com.javasampleapproach.fcm"
}

III. Source Code

SpringBootXMPPServer



By grokonez | July 13, 2017.


Related Posts


6 thoughts on “Firebase Cloud Messaging – XMPP Server example to receive Upstream Messages | Spring Integration”

  1. Hi, thanks for this awesome tutorial. However I am getting this error when I try to send upstream XMPP message from another app.

    org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field ‘json’ cannot be found on object of type ‘org.jivesoftware.smack.packet.StandardExtensionElement’ – maybe not public or not valid?
    at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:217) ~[spring-expression-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:104) ~[spring-expression-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.expression.spel.ast.PropertyOrFieldReference.access$000(PropertyOrFieldReference.java:51) ~[spring-expression-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.expression.spel.ast.PropertyOrFieldReference$AccessorLValue.getValue(PropertyOrFieldReference.java:406) ~[spring-expression-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:90) ~[spring-expression-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:111) ~[spring-expression-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:328) ~[spring-expression-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.integration.xmpp.inbound.ChatMessageListeningEndpoint$ChatMessagePublishingStanzaListener.processStanza(ChatMessageListeningEndpoint.java:143) ~[spring-integration-xmpp-5.1.1.RELEASE.jar:5.1.1.RELEASE]
    at org.jivesoftware.smack.AbstractXMPPConnection$5.run(AbstractXMPPConnection.java:1198) ~[smack-core-4.3.1.jar:4.3.1]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_05]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_05]
    at java.lang.Thread.run(Thread.java:745) [na:1.8.0_05]

  2. Can you please provide us with an example on how to send message to Firebase Cloud Messaging with XMPP and HTTP and using Spring integration?

  3. how can i do downstream message. please?
    i do not know how to send message from spring integration xmpp to android app.

Got Something To Say:

Your email address will not be published. Required fields are marked *

*