INT-3963: Add XMPP Extensions Support
JIRA: https://jira.spring.io/browse/INT-3963 * Update to Smack-4.1.6 * Introduce `stanza-filter` option for the `<int-xmpp:inbound-channel-adapter>` * Introduce `payloadExpression` for the complex and specific `stanza` parsing, e.g. GCM packets * Deprecate `extract-payload` in favor of `payload-expression` * Add `ChatMessageListeningEndpointTests` test for GCM protocol * Add `ChatMessageInboundChannelAdapterParser` test for new attributes * Document changes Polishing according PR comments Extract `#extension` SpEL variable Document the `#extension` SpEL variable
This commit is contained in:
committed by
Gary Russell
parent
956cf275e1
commit
f7c59b3b18
@@ -9,24 +9,35 @@
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">
|
||||
|
||||
<beans:bean id="testConnection" class="org.mockito.Mockito" factory-method="mock">
|
||||
<beans:constructor-arg value="org.jivesoftware.smack.XMPPConnection"/>
|
||||
<beans:bean id="testConnection" class="org.mockito.Mockito" factory-method="spy">
|
||||
<beans:constructor-arg>
|
||||
<beans:bean class="org.jivesoftware.smack.tcp.XMPPTCPConnection">
|
||||
<beans:constructor-arg value="guest"/>
|
||||
<beans:constructor-arg value="guest"/>
|
||||
</beans:bean>
|
||||
</beans:constructor-arg>
|
||||
</beans:bean>
|
||||
|
||||
<channel id="xmppInbound">
|
||||
<queue/>
|
||||
</channel>
|
||||
|
||||
<beans:bean id="stanzaFilter" class="org.mockito.Mockito" factory-method="mock">
|
||||
<beans:constructor-arg value="org.jivesoftware.smack.filter.StanzaFilter"/>
|
||||
</beans:bean>
|
||||
|
||||
|
||||
<xmpp:inbound-channel-adapter id="xmppInboundAdapter" channel="xmppInbound"
|
||||
xmpp-connection="testConnection" extract-payload="false"
|
||||
auto-startup="false" error-channel="errorChannel"
|
||||
mapped-request-headers="foo*, xmpp*"/>
|
||||
xmpp-connection="testConnection" payload-expression="#root"
|
||||
auto-startup="false" error-channel="errorChannel"
|
||||
mapped-request-headers="foo*, xmpp*"
|
||||
stanza-filter="stanzaFilter"/>
|
||||
|
||||
<xmpp:inbound-channel-adapter id="autoChannel"
|
||||
xmpp-connection="testConnection" extract-payload="false"
|
||||
auto-startup="false" error-channel="errorChannel"
|
||||
mapped-request-headers="foo*, xmpp*"/>
|
||||
xmpp-connection="testConnection" extract-payload="false"
|
||||
auto-startup="false" error-channel="errorChannel"
|
||||
mapped-request-headers="foo*, xmpp*"/>
|
||||
|
||||
<bridge input-channel="autoChannel" output-channel="nullChannel" />
|
||||
<bridge input-channel="autoChannel" output-channel="nullChannel"/>
|
||||
|
||||
</beans:beans>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,6 +21,7 @@ import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||
import org.jivesoftware.smack.StanzaListener;
|
||||
@@ -34,10 +35,10 @@ import org.mockito.Mockito;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.integration.xmpp.inbound.ChatMessageListeningEndpoint;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.annotation.DirtiesContext.ClassMode;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
@@ -49,6 +50,7 @@ import org.springframework.util.ReflectionUtils;
|
||||
* @author Mark Fisher
|
||||
* @author Gunnar Hillert
|
||||
* @author Florian Schmaus
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@@ -69,6 +71,7 @@ public class ChatMessageInboundChannelAdapterParserTests {
|
||||
private ChatMessageListeningEndpoint autoChannelAdapter;
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void testInboundAdapter() {
|
||||
ChatMessageListeningEndpoint adapter = context.getBean("xmppInboundAdapter", ChatMessageListeningEndpoint.class);
|
||||
MessageChannel errorChannel = (MessageChannel) TestUtils.getPropertyValue(adapter, "errorChannel");
|
||||
@@ -77,7 +80,16 @@ public class ChatMessageInboundChannelAdapterParserTests {
|
||||
QueueChannel channel = (QueueChannel) TestUtils.getPropertyValue(adapter, "outputChannel");
|
||||
assertEquals("xmppInbound", channel.getComponentName());
|
||||
XMPPConnection connection = (XMPPConnection) TestUtils.getPropertyValue(adapter, "xmppConnection");
|
||||
assertEquals(connection, context.getBean("testConnection"));
|
||||
assertSame(connection, context.getBean("testConnection"));
|
||||
Object stanzaFilter = context.getBean("stanzaFilter");
|
||||
assertSame(stanzaFilter, TestUtils.getPropertyValue(adapter, "stanzaFilter"));
|
||||
assertEquals("#root", TestUtils.getPropertyValue(adapter, "payloadExpression.expression"));
|
||||
adapter.start();
|
||||
Map asyncRecvListeners = TestUtils.getPropertyValue(connection, "asyncRecvListeners", Map.class);
|
||||
assertEquals(1, asyncRecvListeners.size());
|
||||
assertSame(stanzaFilter,
|
||||
TestUtils.getPropertyValue(asyncRecvListeners.values().iterator().next(), "packetFilter"));
|
||||
adapter.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -16,26 +16,40 @@
|
||||
|
||||
package org.springframework.integration.xmpp.inbound;
|
||||
|
||||
import static org.hamcrest.core.IsInstanceOf.instanceOf;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.willAnswer;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||
import org.jivesoftware.smack.StanzaListener;
|
||||
import org.jivesoftware.smack.XMPPConnection;
|
||||
import org.jivesoftware.smack.filter.StanzaFilter;
|
||||
import org.jivesoftware.smack.packet.Message;
|
||||
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
|
||||
import org.jivesoftware.smack.util.PacketParserUtils;
|
||||
import org.jivesoftware.smackx.gcm.packet.GcmPacketExtension;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
@@ -63,7 +77,7 @@ public class ChatMessageListeningEndpointTests {
|
||||
XMPPConnection connection = mock(XMPPConnection.class);
|
||||
ChatMessageListeningEndpoint endpoint = new ChatMessageListeningEndpoint(connection);
|
||||
|
||||
doAnswer(new Answer<Object>() {
|
||||
willAnswer(new Answer<Object>() {
|
||||
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
@@ -71,17 +85,19 @@ public class ChatMessageListeningEndpointTests {
|
||||
return null;
|
||||
}
|
||||
|
||||
}).when(connection).addAsyncStanzaListener(Mockito.any(StanzaListener.class), Mockito.any(StanzaFilter.class));
|
||||
}).given(connection)
|
||||
.addAsyncStanzaListener(Mockito.any(StanzaListener.class), Mockito.any(StanzaFilter.class));
|
||||
|
||||
doAnswer(new Answer<Object>() {
|
||||
willAnswer(new Answer<Object>() {
|
||||
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
packetListSet.remove((StanzaListener) invocation.getArguments()[0]);
|
||||
packetListSet.remove(invocation.getArguments()[0]);
|
||||
return null;
|
||||
}
|
||||
|
||||
}).when(connection).removeAsyncStanzaListener(Mockito.any(StanzaListener.class));
|
||||
}).given(connection)
|
||||
.removeAsyncStanzaListener(Mockito.any(StanzaListener.class));
|
||||
|
||||
assertEquals(0, packetListSet.size());
|
||||
endpoint.setOutputChannel(new QueueChannel());
|
||||
@@ -151,4 +167,110 @@ public class ChatMessageListeningEndpointTests {
|
||||
assertEquals("hello", ((MessagingException) msg.getPayload()).getFailedMessage().getPayload());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("deprecation")
|
||||
public void testExpression() throws Exception {
|
||||
TestXMPPConnection testXMPPConnection = new TestXMPPConnection();
|
||||
|
||||
QueueChannel inputChannel = new QueueChannel();
|
||||
|
||||
ChatMessageListeningEndpoint endpoint = new ChatMessageListeningEndpoint(testXMPPConnection);
|
||||
endpoint.setExtractPayload(false);
|
||||
endpoint.setOutputChannel(inputChannel);
|
||||
endpoint.setBeanFactory(mock(BeanFactory.class));
|
||||
endpoint.afterPropertiesSet();
|
||||
endpoint.start();
|
||||
|
||||
Message smackMessage = new Message();
|
||||
smackMessage.setBody("foo");
|
||||
|
||||
XmlPullParser xmlPullParser = PacketParserUtils.newXmppParser(new StringReader(smackMessage.toString()));
|
||||
xmlPullParser.next();
|
||||
testXMPPConnection.parseAndProcessStanza(xmlPullParser);
|
||||
|
||||
org.springframework.messaging.Message<?> receive = inputChannel.receive(10000);
|
||||
assertNotNull(receive);
|
||||
|
||||
Object payload = receive.getPayload();
|
||||
assertThat(payload, instanceOf(Message.class));
|
||||
assertEquals(smackMessage.getStanzaId(), ((Message) payload).getStanzaId());
|
||||
assertEquals(smackMessage.getBody(), ((Message) payload).getBody());
|
||||
|
||||
Log logger = Mockito.spy(TestUtils.getPropertyValue(endpoint, "logger", Log.class));
|
||||
given(logger.isInfoEnabled()).willReturn(true);
|
||||
|
||||
new DirectFieldAccessor(endpoint).setPropertyValue("logger", logger);
|
||||
|
||||
endpoint.setPayloadExpression(null);
|
||||
|
||||
smackMessage = new Message();
|
||||
xmlPullParser = PacketParserUtils.newXmppParser(new StringReader(smackMessage.toString()));
|
||||
xmlPullParser.next();
|
||||
testXMPPConnection.parseAndProcessStanza(xmlPullParser);
|
||||
|
||||
ArgumentCaptor<String> argumentCaptor = new ArgumentCaptor<String>();
|
||||
|
||||
verify(logger).info(argumentCaptor.capture());
|
||||
|
||||
|
||||
assertEquals("The XMPP Message [" + smackMessage + "] with empty body is ignored.",
|
||||
argumentCaptor.getValue());
|
||||
|
||||
endpoint.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGcmExtension() throws Exception {
|
||||
String data = "{\n" +
|
||||
" \"to\":\"me\",\n" +
|
||||
" \"notification\": {\n" +
|
||||
" \"title\": \"Something interesting\",\n" +
|
||||
" \"text\": \"Here we go\"\n" +
|
||||
" },\n" +
|
||||
" \"time_to_live\":\"600\"\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
GcmPacketExtension packetExtension = new GcmPacketExtension(data);
|
||||
Message smackMessage = new Message();
|
||||
smackMessage.addExtension(packetExtension);
|
||||
|
||||
TestXMPPConnection testXMPPConnection = new TestXMPPConnection();
|
||||
|
||||
QueueChannel inputChannel = new QueueChannel();
|
||||
|
||||
ChatMessageListeningEndpoint endpoint = new ChatMessageListeningEndpoint(testXMPPConnection);
|
||||
Expression payloadExpression = new SpelExpressionParser().parseExpression("#extension.json");
|
||||
endpoint.setPayloadExpression(payloadExpression);
|
||||
endpoint.setOutputChannel(inputChannel);
|
||||
endpoint.setBeanFactory(mock(BeanFactory.class));
|
||||
endpoint.afterPropertiesSet();
|
||||
endpoint.start();
|
||||
|
||||
XmlPullParser xmlPullParser = PacketParserUtils.newXmppParser(new StringReader(smackMessage.toString()));
|
||||
xmlPullParser.next();
|
||||
testXMPPConnection.parseAndProcessStanza(xmlPullParser);
|
||||
|
||||
org.springframework.messaging.Message<?> receive = inputChannel.receive(10000);
|
||||
assertNotNull(receive);
|
||||
|
||||
assertEquals(data, receive.getPayload());
|
||||
|
||||
endpoint.stop();
|
||||
}
|
||||
|
||||
private static class TestXMPPConnection extends XMPPTCPConnection {
|
||||
|
||||
private TestXMPPConnection() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseAndProcessStanza(XmlPullParser parser) throws Exception {
|
||||
super.parseAndProcessStanza(parser);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user