INT-1592 added support for auto-discovery of XmppConnection based on the bean name - 'xmppConnection', added tests

This commit is contained in:
Oleg Zhurakousky
2010-11-09 10:26:17 -05:00
parent 470bd96311
commit 1fdafb547f
12 changed files with 273 additions and 53 deletions

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2002-2010 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;
import org.jivesoftware.smack.XMPPConnection;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.integration.endpoint.AbstractEndpoint;
import org.springframework.util.Assert;
/**
* @author Oleg Zhurakousky
* @since 2.0
*
*/
public abstract class AbstractXmppConnectionAwareEndpoint extends AbstractEndpoint {
protected volatile XMPPConnection xmppConnection;
protected volatile boolean initialized;
public AbstractXmppConnectionAwareEndpoint(){}
public AbstractXmppConnectionAwareEndpoint(XMPPConnection xmppConnection){
Assert.notNull(xmppConnection, "'xmppConnection' must no be null");
this.xmppConnection = xmppConnection;
}
protected void onInit() throws Exception {
BeanFactory bf = this.getBeanFactory();
if (xmppConnection == null && bf != null){
xmppConnection = bf.getBean(XmppContextUtils.XMPP_CONNECTION_BEAN_NAME, XMPPConnection.class);
}
Assert.notNull(xmppConnection, "Failed to resolve XMPPConnection. XMPPConnection must either be set expicitly " +
"via 'xmpp-connection' attribute or implicitly by registering a bean with the name 'xmppConnection' and of type " +
"'org.jivesoftware.smack.XMPPConnection' in the Application Context");
this.initialized = true;
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2002-2010 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;
import org.jivesoftware.smack.XMPPConnection;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.integration.handler.AbstractMessageHandler;
import org.springframework.util.Assert;
/**
* @author Oleg Zhurakousky
* @since 2.0
*
*/
public abstract class AbstractXmppConnectionAwareMessageHandler extends AbstractMessageHandler {
protected volatile XMPPConnection xmppConnection;
protected volatile boolean initialized;
public AbstractXmppConnectionAwareMessageHandler(){}
public AbstractXmppConnectionAwareMessageHandler(XMPPConnection xmppConnection){
Assert.notNull(xmppConnection, "'xmppConnection' must no be null");
this.xmppConnection = xmppConnection;
}
protected void onInit() throws Exception {
BeanFactory bf = this.getBeanFactory();
if (xmppConnection == null && bf != null){
xmppConnection = bf.getBean(XmppContextUtils.XMPP_CONNECTION_BEAN_NAME, XMPPConnection.class);
}
Assert.notNull(xmppConnection, "Failed to resolve XMPPConnection. XMPPConnection must either be set expicitly " +
"via 'xmpp-connection' attribute or implicitly by registering a bean with the name 'xmppConnection' and of type " +
"'org.jivesoftware.smack.XMPPConnection' in the Application Context");
this.initialized = true;
}
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright 2002-2010 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;
/**
* @author Oleg ZHurakousky
* @since 2.0
*/
public interface XmppContextUtils {
final String XMPP_CONNECTION_BEAN_NAME = "xmppConnection";
}

View File

@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.xmpp.messages;
import org.jivesoftware.smack.Chat;
@@ -24,8 +23,8 @@ import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.core.MessagingTemplate;
import org.springframework.integration.endpoint.AbstractEndpoint;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.xmpp.AbstractXmppConnectionAwareEndpoint;
import org.springframework.integration.xmpp.XmppHeaders;
import org.springframework.util.Assert;
@@ -53,22 +52,22 @@ import org.springframework.util.Assert;
* @see XMPPConnection the XMPPConnection (as
* created by {@link XmppConnectionFactory}
*/
public class XmppMessageDrivenEndpoint extends AbstractEndpoint {
public class XmppMessageDrivenEndpoint extends AbstractXmppConnectionAwareEndpoint {
private final MessagingTemplate messagingTemplate = new MessagingTemplate();
private volatile MessageChannel requestChannel;
private final XMPPConnection xmppConnection;
private volatile boolean extractPayload = true;
private volatile PacketListener packetListener;
private volatile boolean initialized;
public XmppMessageDrivenEndpoint(){
super();
}
public XmppMessageDrivenEndpoint(XMPPConnection xmppConnection){
this.xmppConnection = xmppConnection;
super(xmppConnection);
}
/**
@@ -89,7 +88,7 @@ public class XmppMessageDrivenEndpoint extends AbstractEndpoint {
@Override
protected void doStart() {
Assert.isTrue(this.initialized, this.getComponentType() + " must be initialized");
Assert.isTrue(this.initialized, this.getComponentName() + "#" + this.getComponentType() + " must be initialized");
xmppConnection.addPacketListener(this.packetListener, null);
}
@@ -100,6 +99,7 @@ public class XmppMessageDrivenEndpoint extends AbstractEndpoint {
@Override
protected void onInit() throws Exception {
super.onInit();
this.messagingTemplate.setDefaultChannel(requestChannel);
this.messagingTemplate.afterPropertiesSet();
this.packetListener = new PacketListener() {
@@ -108,7 +108,6 @@ public class XmppMessageDrivenEndpoint extends AbstractEndpoint {
forwardXmppMessage(xmppConnection.getChatManager().getThreadChat(message.getThread()), message);
}
};
this.initialized = true;
}
private void forwardXmppMessage(Chat chat, Message xmppMessage) {

View File

@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.xmpp.messages;
import org.jivesoftware.smack.Chat;
@@ -21,7 +20,7 @@ import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.springframework.integration.Message;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.handler.AbstractMessageHandler;
import org.springframework.integration.xmpp.AbstractXmppConnectionAwareMessageHandler;
import org.springframework.integration.xmpp.XmppHeaders;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -32,30 +31,30 @@ import org.springframework.util.StringUtils;
* @author Oleg Zhurakousky
* @since 2.0
*/
public class XmppMessageSendingMessageHandler extends AbstractMessageHandler {
private final XMPPConnection xmppConnection;
public class XmppMessageSendingMessageHandler extends AbstractXmppConnectionAwareMessageHandler {
public XmppMessageSendingMessageHandler(){
super();
}
public XmppMessageSendingMessageHandler(XMPPConnection xmppConnection){
Assert.notNull(xmppConnection, "'xmppConnection' must no be null");
this.xmppConnection = xmppConnection;
super(xmppConnection);
}
protected void handleMessageInternal(Message<?> message) {
String messageBody = null;
String destinationUser = null;
Object payload = message.getPayload();
Assert.isInstanceOf(String.class, payload, "Only payload of type String is suported. You " +
"can apply transformer prior to sending message to this handler");
messageBody = (String) payload;
destinationUser = (String) message.getHeaders().get(XmppHeaders.CHAT_TO_USER);
Assert.isTrue(this.initialized, this.getComponentName() + "#" + this.getComponentType() + " must be initialized");
Object messageBody = message.getPayload();
String destinationUser = (String) message.getHeaders().get(XmppHeaders.CHAT_TO_USER);
Assert.state(StringUtils.hasText(destinationUser), "'" + XmppHeaders.CHAT_TO_USER + "' header must not be null");
Assert.isInstanceOf(String.class, messageBody, "Only payload of type String is suported. You " +
"can apply transformer prior to sending message to this handler");
String threadId = (String) message.getHeaders().get(XmppHeaders.CHAT_THREAD_ID);
Chat chat = getOrCreateChatWithParticipant(destinationUser, threadId);
// TODO - figure out what to do with chat.threadId?
try {
chat.sendMessage(messageBody);
} catch (XMPPException e) {
chat.sendMessage((String) messageBody);
}
catch (XMPPException e) {
throw new MessageHandlingException(message, e);
}
}

View File

@@ -25,13 +25,15 @@ import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.RosterListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.packet.Presence;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.MessagingException;
import org.springframework.integration.core.MessagingTemplate;
import org.springframework.integration.endpoint.AbstractEndpoint;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.xmpp.AbstractXmppConnectionAwareEndpoint;
import org.springframework.integration.xmpp.XmppContextUtils;
import org.springframework.util.Assert;
/**
@@ -43,22 +45,22 @@ import org.springframework.util.Assert;
* @author Oleg Zhurakousky
* @since 2.0
*/
public class XmppRosterEventMessageDrivenEndpoint extends AbstractEndpoint {
public class XmppRosterEventMessageDrivenEndpoint extends AbstractXmppConnectionAwareEndpoint {
private static final Log logger = LogFactory.getLog(XmppRosterEventMessageDrivenEndpoint.class);
private volatile MessageChannel requestChannel;
private final XMPPConnection xmppConnection;
private final MessagingTemplate messagingTemplate = new MessagingTemplate();
private final EventForwardingRosterListener rosterListener = new EventForwardingRosterListener();
private volatile boolean initialized;
public XmppRosterEventMessageDrivenEndpoint(){
super();
}
public XmppRosterEventMessageDrivenEndpoint(XMPPConnection xmppConnection){
this.xmppConnection = xmppConnection;
super(xmppConnection);
}
/**
* @param requestChannel the channel on which the inbound message should be sent
@@ -69,7 +71,7 @@ public class XmppRosterEventMessageDrivenEndpoint extends AbstractEndpoint {
@Override
protected void doStart() {
Assert.isTrue(this.initialized, this.getComponentType() + " must be initialized");
Assert.isTrue(this.initialized, this.getComponentName() + "#" + this.getComponentType() + " must be initialized");
this.xmppConnection.getRoster().addRosterListener(rosterListener);
}
@@ -80,9 +82,9 @@ public class XmppRosterEventMessageDrivenEndpoint extends AbstractEndpoint {
@Override
protected void onInit() throws Exception {
super.onInit();
this.messagingTemplate.setDefaultChannel(requestChannel);
this.messagingTemplate.afterPropertiesSet();
this.initialized = true;
}
/**

View File

@@ -18,33 +18,30 @@ package org.springframework.integration.xmpp.presence;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.packet.Presence;
import org.springframework.integration.Message;
import org.springframework.integration.handler.AbstractMessageHandler;
import org.springframework.integration.mapping.OutboundMessageMapper;
import org.springframework.integration.xmpp.AbstractXmppConnectionAwareMessageHandler;
import org.springframework.util.Assert;
/**
* This class will facilitate publishing updated presence values for a given connection. This change happens on the
* {@link org.jivesoftware.smack.Roster#setSubscriptionMode(org.jivesoftware.smack.Roster.SubscriptionMode)} property.
* This class will facilitate publishing updated presence values for a given connection.
*
* @author Josh Long
* @author Oleg Zhurakousky
* @see org.jivesoftware.smack.packet.Presence.Mode the mode (i.e.:
* {@link org.jivesoftware.smack.packet.Presence.Mode#away})
* @see org.jivesoftware.smack.packet.Presence.Type the type (i.e.:
* {@link org.jivesoftware.smack.packet.Presence.Type#available} )
*
* @since 2.0
*/
public class XmppRosterEventMessageSendingHandler extends AbstractMessageHandler {
public class XmppRosterEventMessageSendingHandler extends AbstractXmppConnectionAwareMessageHandler {
private final XMPPConnection xmppConnection;
public XmppRosterEventMessageSendingHandler(){
super();
}
public XmppRosterEventMessageSendingHandler(XMPPConnection xmppConnection){
Assert.notNull(xmppConnection, "'xmppConnection' must not be null");
this.xmppConnection = xmppConnection;
super(xmppConnection);
}
@Override
protected void handleMessageInternal(Message<?> message) throws Exception {
Assert.isTrue(this.initialized, this.getComponentName() + "#" + this.getComponentType() + " must be initialized");
Object payload = message.getPayload();
Assert.isInstanceOf(Presence.class, payload, "'payload' must be of type 'org.jivesoftware.smack.packet.Presence', was "
+ payload.getClass().getName());

View File

@@ -66,7 +66,7 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="xmpp-connection" type="xsd:string" use="required">
<xsd:attribute name="xmpp-connection" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
@@ -97,7 +97,7 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="xmpp-connection" type="xsd:string" use="required">
<xsd:attribute name="xmpp-connection" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
@@ -128,7 +128,7 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="xmpp-connection" type="xsd:string" use="required">
<xsd:attribute name="xmpp-connection" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
@@ -160,7 +160,7 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="xmpp-connection" type="xsd:string" use="required">
<xsd:attribute name="xmpp-connection" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">

View File

@@ -16,6 +16,7 @@
package org.springframework.integration.xmpp.messages;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
@@ -29,6 +30,9 @@ import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.integration.xmpp.XmppContextUtils;
/**
* @author Oleg Zhurakousky
@@ -76,4 +80,20 @@ public class XmppMessageDrivenEndpointTests {
XmppMessageDrivenEndpoint endpoint = new XmppMessageDrivenEndpoint(mock(XMPPConnection.class));
endpoint.start();
}
@Test
public void testWithImplicitXmppConnection(){
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.registerSingleton(XmppContextUtils.XMPP_CONNECTION_BEAN_NAME, mock(XMPPConnection.class));
XmppMessageDrivenEndpoint endpoint = new XmppMessageDrivenEndpoint();
endpoint.setBeanFactory(bf);
endpoint.afterPropertiesSet();
assertNotNull(TestUtils.getPropertyValue(endpoint,"xmppConnection"));
}
@Test(expected=IllegalArgumentException.class)
public void testNoXmppConnection(){
XmppMessageDrivenEndpoint endpoint = new XmppMessageDrivenEndpoint();
endpoint.afterPropertiesSet();
}
}

View File

@@ -3,6 +3,7 @@
*/
package org.springframework.integration.xmpp.messages;
import static junit.framework.Assert.assertNotNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
@@ -15,10 +16,13 @@ import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.XMPPConnection;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.integration.Message;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.integration.xmpp.XmppContextUtils;
import org.springframework.integration.xmpp.XmppHeaders;
/**
@@ -37,7 +41,7 @@ public class XmppMessageSendingMessageHandlerTests {
when(chantManager.createChat(Mockito.any(String.class), Mockito.any(MessageListener.class))).thenReturn(chat);
XmppMessageSendingMessageHandler handler = new XmppMessageSendingMessageHandler(connection);
handler.afterPropertiesSet();
Message<?> message = MessageBuilder.withPayload("Test Message").
setHeader(XmppHeaders.CHAT_TO_USER, "kermit@frog.com").
build();
@@ -73,4 +77,19 @@ public class XmppMessageSendingMessageHandlerTests {
XmppMessageSendingMessageHandler handler = new XmppMessageSendingMessageHandler(mock(XMPPConnection.class));
handler.handleMessage(new GenericMessage<Integer>(123));
}
@Test
public void testWithImplicitXmppConnection(){
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.registerSingleton(XmppContextUtils.XMPP_CONNECTION_BEAN_NAME, mock(XMPPConnection.class));
XmppMessageSendingMessageHandler handler = new XmppMessageSendingMessageHandler();
handler.setBeanFactory(bf);
handler.afterPropertiesSet();
assertNotNull(TestUtils.getPropertyValue(handler,"xmppConnection"));
}
@Test(expected=IllegalArgumentException.class)
public void testNoXmppConnection(){
XmppMessageSendingMessageHandler handler = new XmppMessageSendingMessageHandler();
handler.afterPropertiesSet();
}
}

View File

@@ -15,10 +15,52 @@
*/
package org.springframework.integration.xmpp.messages;
import static junit.framework.Assert.assertNotNull;
import static org.mockito.Mockito.mock;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.packet.Presence;
import org.junit.Test;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.integration.xmpp.XmppContextUtils;
import org.springframework.integration.xmpp.presence.XmppRosterEventMessageSendingHandler;
/**
* @author Oleg Zhurakousky
*
*/
public class XmppRosterEventMessageSendingHandlerTests {
@Test
public void testPresencePayload(){
XmppRosterEventMessageSendingHandler handler = new XmppRosterEventMessageSendingHandler(mock(XMPPConnection.class));
handler.afterPropertiesSet();
handler.handleMessage(new GenericMessage(mock(Presence.class)));
}
@Test(expected=MessageHandlingException.class)
public void testWrongPayload(){
XmppRosterEventMessageSendingHandler handler = new XmppRosterEventMessageSendingHandler(mock(XMPPConnection.class));
handler.afterPropertiesSet();
handler.handleMessage(new GenericMessage(new Object()));
}
@Test
public void testWithImplicitXmppConnection(){
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.registerSingleton(XmppContextUtils.XMPP_CONNECTION_BEAN_NAME, mock(XMPPConnection.class));
XmppRosterEventMessageSendingHandler handler = new XmppRosterEventMessageSendingHandler();
handler.setBeanFactory(bf);
handler.afterPropertiesSet();
assertNotNull(TestUtils.getPropertyValue(handler,"xmppConnection"));
}
@Test(expected=IllegalArgumentException.class)
public void testNoXmppConnection(){
XmppRosterEventMessageSendingHandler handler = new XmppRosterEventMessageSendingHandler();
handler.afterPropertiesSet();
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.integration.xmpp.presence;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -35,9 +36,11 @@ import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.integration.Message;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.integration.xmpp.XmppContextUtils;
/**
* @author Oleg Zhurakousky
@@ -115,4 +118,20 @@ public class XmppRosterEventMessageDrivenEndpointTests {
Message<?> message = channel.receive(10);
assertEquals(entries, message.getPayload());
}
@Test
public void testWithImplicitXmppConnection(){
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.registerSingleton(XmppContextUtils.XMPP_CONNECTION_BEAN_NAME, mock(XMPPConnection.class));
XmppRosterEventMessageDrivenEndpoint endpoint = new XmppRosterEventMessageDrivenEndpoint();
endpoint.setBeanFactory(bf);
endpoint.afterPropertiesSet();
assertNotNull(TestUtils.getPropertyValue(endpoint,"xmppConnection"));
}
@Test(expected=IllegalArgumentException.class)
public void testNoXmppConnection(){
XmppRosterEventMessageDrivenEndpoint handler = new XmppRosterEventMessageDrivenEndpoint();
handler.afterPropertiesSet();
}
}