/*
* Copyright 2002-2017 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.xmpp.inbound;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.isNull;
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 java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
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.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.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;
import org.springframework.integration.xmpp.core.XmppContextUtils;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.support.ErrorMessage;
/**
* @author Oleg Zhurakousky
* @author Gunnar Hillert
* @author Florian Schmaus
* @author Artem Bilan
*/
public class ChatMessageListeningEndpointTests {
@Test
/*
* Should add/remove StanzaListener when endpoint started/stopped
*/
public void testLifecycle() {
final Set<StanzaListener> packetListSet = new HashSet<>();
XMPPConnection connection = mock(XMPPConnection.class);
ChatMessageListeningEndpoint endpoint = new ChatMessageListeningEndpoint(connection);
willAnswer(invocation -> {
packetListSet.add(invocation.getArgument(0));
return null;
}).given(connection)
.addAsyncStanzaListener(any(StanzaListener.class), isNull());
willAnswer(invocation -> {
packetListSet.remove(invocation.getArgument(0));
return null;
}).given(connection)
.removeAsyncStanzaListener(any(StanzaListener.class));
assertEquals(0, packetListSet.size());
endpoint.setOutputChannel(new QueueChannel());
endpoint.setBeanFactory(mock(BeanFactory.class));
endpoint.afterPropertiesSet();
endpoint.start();
assertEquals(1, packetListSet.size());
endpoint.stop();
assertEquals(0, packetListSet.size());
}
@Test(expected = IllegalArgumentException.class)
public void testNonInitializationFailure() {
ChatMessageListeningEndpoint endpoint = new ChatMessageListeningEndpoint(mock(XMPPConnection.class));
endpoint.start();
}
@Test
public void testWithImplicitXmppConnection() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.registerSingleton(XmppContextUtils.XMPP_CONNECTION_BEAN_NAME, mock(XMPPConnection.class));
ChatMessageListeningEndpoint endpoint = new ChatMessageListeningEndpoint();
endpoint.setBeanFactory(bf);
endpoint.setOutputChannel(new QueueChannel());
endpoint.afterPropertiesSet();
assertNotNull(TestUtils.getPropertyValue(endpoint, "xmppConnection"));
}
@Test(expected = IllegalArgumentException.class)
public void testNoXmppConnection() {
ChatMessageListeningEndpoint endpoint = new ChatMessageListeningEndpoint();
endpoint.setBeanFactory(mock(BeanFactory.class));
endpoint.afterPropertiesSet();
}
@Test
public void testWithErrorChannel() throws NotConnectedException {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
XMPPConnection connection = mock(XMPPConnection.class);
bf.registerSingleton(XmppContextUtils.XMPP_CONNECTION_BEAN_NAME, connection);
ChatMessageListeningEndpoint endpoint = new ChatMessageListeningEndpoint();
DirectChannel outChannel = new DirectChannel();
outChannel.subscribe(message -> {
throw new RuntimeException("ooops");
});
PollableChannel errorChannel = new QueueChannel();
endpoint.setBeanFactory(bf);
endpoint.setOutputChannel(outChannel);
endpoint.setErrorChannel(errorChannel);
endpoint.afterPropertiesSet();
StanzaListener listener = (StanzaListener) TestUtils.getPropertyValue(endpoint, "stanzaListener");
Message smackMessage = new Message("kermit@frog.com");
smackMessage.setBody("hello");
smackMessage.setThread("1234");
listener.processPacket(smackMessage);
ErrorMessage msg =
(ErrorMessage) errorChannel.receive();
assertEquals("hello", ((MessagingException) msg.getPayload()).getFailedMessage().getPayload());
}
@Test
public void testExpression() throws Exception {
TestXMPPConnection testXMPPConnection = new TestXMPPConnection();
QueueChannel inputChannel = new QueueChannel();
ChatMessageListeningEndpoint endpoint = new ChatMessageListeningEndpoint(testXMPPConnection);
SpelExpressionParser parser = new SpelExpressionParser();
endpoint.setPayloadExpression(parser.parseExpression("#root"));
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);
final CountDownLatch logLatch = new CountDownLatch(1);
willAnswer(invocation -> {
Object result = invocation.callRealMethod();
logLatch.countDown();
return result;
}).given(logger).info(anyString());
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 = ArgumentCaptor.forClass(String.class);
assertTrue(logLatch.await(10, TimeUnit.SECONDS));
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 {
TestXMPPConnection() {
super(null);
}
@Override
protected void parseAndProcessStanza(XmlPullParser parser) throws Exception {
super.parseAndProcessStanza(parser);
}
}
}