SWS-428 - XMPP support

This commit is contained in:
Arjen Poutsma
2010-04-23 14:42:23 +00:00
parent b983fa4543
commit cceba687dd
18 changed files with 1052 additions and 21 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006 the original author or authors.
* Copyright 2005-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.
@@ -18,9 +18,6 @@ package org.springframework.ws.transport.support;
import java.net.URISyntaxException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.springframework.ws.FaultAwareWebServiceMessage;
@@ -37,6 +34,9 @@ import org.springframework.ws.transport.context.DefaultTransportContext;
import org.springframework.ws.transport.context.TransportContext;
import org.springframework.ws.transport.context.TransportContextHolder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Convenience base class for server-side transport objects. Contains a {@link WebServiceMessageFactory}, and has
* methods for handling incoming {@link WebServiceConnection}s.
@@ -111,7 +111,7 @@ public abstract class WebServiceMessageReceiverObjectSupport implements Initiali
private void logUri(WebServiceConnection connection) {
if (logger.isDebugEnabled()) {
try {
logger.debug("Accepting incoming [" + connection + "] to [" + connection.getUri() + "]");
logger.debug("Accepting incoming [" + connection + "] at [" + connection.getUri() + "]");
}
catch (URISyntaxException e) {
// ignore

View File

@@ -634,6 +634,11 @@
<artifactId>activemq-core</artifactId>
<version>4.1.1</version>
</dependency>
<dependency>
<groupId>jivesoftware</groupId>
<artifactId>smack</artifactId>
<version>3.1.0</version>
</dependency>
<!-- Logging dependencies -->
<dependency>
<groupId>commons-logging</groupId>

View File

@@ -182,6 +182,47 @@
<property name="defaultUri" value="mailto:server@example.com?subject=SOAP%20Test"/>
</bean>
</beans>]]></programlisting>
</para>
</section>
<section>
<title>XMPP transport</title>
<para>
Spring Web Services 2.0 introduced an XMPP (Jabber) transport, which can be used to send and
receive web service messages via XMPP. The client-side XMPP
functionality is contained in the <classname>XmppMessageSender</classname> class.
This class creates an XMPP message from the request
<interfacename>WebServiceMessage</interfacename>, and sends it via XMPP. It then listens for a
response message to arrive.
</para>
<para>
To use the <classname>XmppMessageSender</classname>, set the <property>defaultUri</property> or
<parameter>uri</parameter> parameter to a <literal>xmpp</literal> URI, for example
<uri>xmpp:johndoe@jabber.org</uri>. The sender also requires an
<classname>XMPPConnection</classname> to work, which can be conveniently created using the
<classname>org.springframework.ws.transport.xmpp.support.XmppConnectionFactoryBean</classname>.
</para>
<para>
The following example shows how to use the xmpp transport:<programlisting><![CDATA[<beans>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
<bean id="connection" class="org.springframework.ws.transport.xmpp.support.XmppConnectionFactoryBean">
<property name="host" value="jabber.org"/>
<property name="username" value="username"/>
<property name="password" value="password"/>
</bean>
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="messageFactory"/>
<property name="messageSender">
<bean class="org.springframework.ws.transport.xmpp.XmppMessageSender">
<property name="connection" ref="connection"/>
</bean>
</property>
<property name="defaultUri" value="xmpp:user@jabber.org"/>
</bean>
</beans>]]></programlisting>
</para>
</section>

View File

@@ -523,6 +523,50 @@
<ulink url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/remoting/support/SimpleHttpServerFactoryBean.html">Javadoc</ulink>.
</para>
</section>
<section>
<title>XMPP transport</title>
<para>
Finally, Spring Web Services 2.0 introduced support for XMPP, otherwise known as Jabber. The support
is based on the <ulink url="http://www.igniterealtime.org/projects/smack/index.jsp">Smack</ulink>
library.
</para>
<para>
Spring Web Services support for XMPP is very similar to the other transports: there is a a
<classname>XmppMessageSender</classname> for the <classname>WebServiceTemplate</classname> and
and a <classname>XmppMessageReceiver</classname> to use with the
<classname>MessageDispatcher</classname>.
</para>
<para>The following example shows how to set up the server-side XMPP components:
<programlisting><![CDATA[<beans>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
<bean id="connection" class="org.springframework.ws.transport.xmpp.support.XmppConnectionFactoryBean">
<property name="host" value="jabber.org"/>
<property name="username" value="username"/>
<property name="password" value="password"/>
</bean>
<bean id="messagingReceiver" class="org.springframework.ws.transport.xmpp.XmppMessageReceiver">
<property name="messageFactory" ref="messageFactory"/>
<property name="connection" ref="connection"/>
<property name="messageReceiver" ref="messageDispatcher"/>
</bean>
<bean id="messageDispatcher" class="org.springframework.ws.soap.server.SoapMessageDispatcher">
<property name="endpointMappings">
<bean
class="org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping">
<property name="defaultEndpoint">
<bean class="com.example.MyEndpoint"/>
</property>
</bean>
</property>
</bean>
</beans>]]></programlisting>
</para>
</section>
</section>
<section>
<title>Endpoints</title>

View File

@@ -11,20 +11,6 @@
<packaging>jar</packaging>
<name>Spring WS Support</name>
<description>Spring Web Services Support package.</description>
<!--
<repositories>
<repository>
<id>spring-ext</id>
<name>Spring External Dependencies Repository</name>
<url>https://springframework.svn.sourceforge.net/svnroot/springframework/repos/repo-ext/</url>
</repository>
<repository>
<id>java.net-maven-2</id>
<name>Java.net Repository for Maven</name>
<url>http://download.java.net/maven/2/</url>
</repository>
</repositories>
-->
<profiles>
<profile>
<id>jdk15</id>
@@ -103,6 +89,10 @@
<artifactId>mail</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>jivesoftware</groupId>
<artifactId>smack</artifactId>
</dependency>
<!-- Other dependencies -->
<dependency>
<groupId>org.apache.activemq</groupId>

View File

@@ -257,7 +257,8 @@ public class JmsSenderConnection extends AbstractSenderConnection {
}
else if (message != null) {
throw new IllegalArgumentException(
"Wrong message type: [" + message.getClass() + "]. Only BytesMessages can be handled.");
"Wrong message type: [" + message.getClass() + "]. " +
"Only BytesMessages or TextMessages can be handled.");
}
}
catch (JMSException ex) {

View File

@@ -66,7 +66,7 @@ public class MailMessageReceiver extends AbstractAsyncStandaloneMessageReceiver
private MonitoringStrategy monitoringStrategy;
/** Sets the from address to use when sending reponse messages. */
/** Sets the from address to use when sending response messages. */
public void setFrom(String from) throws AddressException {
this.from = new InternetAddress(from);
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2005-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.ws.transport.xmpp;
import java.io.ByteArrayInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.springframework.util.Assert;
import org.jivesoftware.smack.packet.Message;
/**
* Input stream that wraps a {@link Message}.
*
* @author Gildas Cuisinier
* @author Arjen Poutsma
* @since 2.0
*/
class MessageInputStream extends FilterInputStream {
MessageInputStream(Message message, String encoding) throws IOException {
super(createInputStream(message, encoding));
}
private static InputStream createInputStream(Message message, String encoding) throws IOException {
Assert.notNull(message, "'message' must not be null");
Assert.notNull(encoding, "'encoding' must not be null");
String text = message.getBody();
byte[] contents = text != null ? text.getBytes(encoding) : new byte[0];
return new ByteArrayInputStream(contents);
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2005-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.ws.transport.xmpp;
import java.io.ByteArrayOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import org.springframework.util.Assert;
import org.jivesoftware.smack.packet.Message;
/**
* Output stream that wraps a {@link Message}.
*
* @author Gildas Cuisinier
* @author Arjen Poutsma
* @since 2.0
*/
class MessageOutputStream extends FilterOutputStream {
private final Message message;
private final String encoding;
MessageOutputStream(Message message, String encoding) {
super(new ByteArrayOutputStream());
Assert.notNull(message, "'message' must not be null");
Assert.notNull(encoding, "'encoding' must not be null");
this.message = message;
this.encoding = encoding;
}
@Override
public void flush() throws IOException {
super.flush();
ByteArrayOutputStream bos = (ByteArrayOutputStream) out;
String text = new String(bos.toByteArray(), encoding);
message.setBody(text);
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright 2005-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.ws.transport.xmpp;
import org.springframework.ws.transport.support.AbstractStandaloneMessageReceiver;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
/**
* Server-side component for receiving XMPP (Jabber) messages. Requires a {@linkplain #setConnection(XMPPConnection)
* connection} to be set, in addition to the {@link #setMessageFactory(org.springframework.ws.WebServiceMessageFactory)
* messageFactory} and {@link #setMessageReceiver(org.springframework.ws.transport.WebServiceMessageReceiver)
* messageReceiver} required by the base class.
*
* @author Gildas Cuisinier
* @author Arjen Poutsma
* @see org.springframework.ws.transport.xmpp.support.XmppConnectionFactoryBean
* @since 2.0
*/
public class XmppMessageReceiver extends AbstractStandaloneMessageReceiver {
/** Default encoding used to read from and write to {@link org.jivesoftware.smack.packet.Message} messages. */
public static final String DEFAULT_MESSAGE_ENCODING = "UTF-8";
private XMPPConnection connection;
private WebServicePacketListener packetListener;
private String messageEncoding = DEFAULT_MESSAGE_ENCODING;
public XmppMessageReceiver() {
}
/** Sets the {@code XMPPConnection} to use. Setting this property is required. */
public void setConnection(XMPPConnection connection) {
this.connection = connection;
}
@Override
protected void onActivate() throws XMPPException {
if (!connection.isConnected()) {
connection.connect();
}
}
@Override
protected void onStart() {
if (logger.isInfoEnabled()) {
logger.info("Starting XMPP receiver [" + connection.getUser() + "]");
}
packetListener = new WebServicePacketListener();
PacketFilter packetFilter = new PacketTypeFilter(Message.class);
connection.addPacketListener(packetListener, packetFilter);
}
@Override
protected void onStop() {
if (logger.isInfoEnabled()) {
logger.info("Stopping XMPP receiver [" + connection.getUser() + "]");
}
connection.removePacketListener(packetListener);
packetListener = null;
}
@Override
protected void onShutdown() {
if (logger.isInfoEnabled()) {
logger.info("Shutting down XMPP receiver [" + connection.getUser() + "]");
}
if (connection.isConnected()) {
connection.disconnect();
}
}
private class WebServicePacketListener implements PacketListener {
public void processPacket(Packet packet) {
logger.info("Received " + packet);
if (packet instanceof Message) {
Message message = (Message) packet;
try {
XmppReceiverConnection wsConnection = new XmppReceiverConnection(connection, message);
wsConnection.setMessageEncoding(messageEncoding);
handleConnection(wsConnection);
}
catch (Exception ex) {
logger.error(ex);
}
}
}
}
}

View File

@@ -0,0 +1,98 @@
/*
* Copyright 2005-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.ws.transport.xmpp;
import java.io.IOException;
import java.net.URI;
import java.util.UUID;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.springframework.ws.transport.WebServiceConnection;
import org.springframework.ws.transport.WebServiceMessageSender;
import org.springframework.ws.transport.xmpp.support.XmppTransportUtils;
import org.jivesoftware.smack.XMPPConnection;
/**
* {@link WebServiceMessageSender} implementation that uses XMPP {@link org.jivesoftware.smack.packet.Message}s.
* Requires a {@link #setConnection(org.jivesoftware.smack.XMPPConnection) connection}to be set.
* <p/>
* This message sender supports URI's of the following format: <blockquote> <tt><b>xmpp:</b></tt><i>to</i> </blockquote>
* The <i>to</i> represents a Jabber ID.
*
* @author Gildas Cuisinier
* @author Arjen Poutsma
* @since 2.0
*/
public class XmppMessageSender implements WebServiceMessageSender, InitializingBean {
/** Default timeout for receive operations: -1 indicates a blocking receive without timeout. */
public static final long DEFAULT_RECEIVE_TIMEOUT = -1;
/** Default encoding used to read from and write to {@link org.jivesoftware.smack.packet.Message} messages. */
public static final String DEFAULT_MESSAGE_ENCODING = "UTF-8";
private long receiveTimeout = DEFAULT_RECEIVE_TIMEOUT;
private String messageEncoding = DEFAULT_MESSAGE_ENCODING;
private XMPPConnection connection;
/** Sets the {@code XMPPConnection}. Setting this property is required. */
public void setConnection(XMPPConnection connection) {
this.connection = connection;
}
/**
* Set the timeout to use for receive calls. The default is -1, which means no timeout.
*
* @see org.jivesoftware.smack.PacketCollector#nextResult(long)
*/
public void setReceiveTimeout(long receiveTimeout) {
this.receiveTimeout = receiveTimeout;
}
/**
* Sets the encoding used to read from {@link org.jivesoftware.smack.packet.Message} object. Defaults to
* <code>UTF-8</code>.
*/
public void setMessageEncoding(String messageEncoding) {
this.messageEncoding = messageEncoding;
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(connection, "'connection' is required");
}
public WebServiceConnection createConnection(URI uri) throws IOException {
String to = XmppTransportUtils.getTo(uri);
String thread = createThread();
XmppSenderConnection connection = new XmppSenderConnection(this.connection, to, thread);
connection.setReceiveTimeout(receiveTimeout);
connection.setMessageEncoding(messageEncoding);
return connection;
}
public boolean supports(URI uri) {
return uri.getScheme().equals(XmppTransportConstants.XMPP_URI_SCHEME);
}
protected String createThread() {
return UUID.randomUUID().toString();
}
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright 2005-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.ws.transport.xmpp;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Iterator;
import org.springframework.util.Assert;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.transport.AbstractReceiverConnection;
import org.springframework.ws.transport.xmpp.support.XmppTransportUtils;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.packet.Message;
/**
* Implementation of {@link org.springframework.ws.transport.WebServiceConnection} that is used for server-side XMPP
* access. Exposes a {@link Message} request and response message.
*
* @author Gildas Cuisinier
* @author Arjen Poutsma
* @since 2.0
*/
public class XmppReceiverConnection extends AbstractReceiverConnection {
private final XMPPConnection connection;
private final Message requestMessage;
private Message responseMessage;
private String messageEncoding;
public XmppReceiverConnection(XMPPConnection connection, Message requestMessage) {
Assert.notNull(connection, "'connection' must not be null");
Assert.notNull(requestMessage, "'requestMessage' must not be null");
this.connection = connection;
this.requestMessage = requestMessage;
}
/** Returns the request message for this connection. */
public Message getRequestMessage() {
return requestMessage;
}
/** Returns the response message, if any, for this connection. */
public Message getResponseMessage() {
return responseMessage;
}
/*
* Package-friendly setters
*/
void setMessageEncoding(String messageEncoding) {
this.messageEncoding = messageEncoding;
}
/*
* URI
*/
public URI getUri() throws URISyntaxException {
return XmppTransportUtils.toUri(requestMessage);
}
/*
* Errors
*/
public boolean hasError() {
return XmppTransportUtils.hasError(responseMessage);
}
public String getErrorMessage() {
return XmppTransportUtils.getErrorMessage(responseMessage);
}
/*
* Receiving
*/
@Override
protected Iterator<String> getRequestHeaderNames() throws IOException {
return XmppTransportUtils.getHeaderNames(requestMessage);
}
@Override
protected Iterator<String> getRequestHeaders(String name) throws IOException {
return XmppTransportUtils.getHeaders(requestMessage, name);
}
@Override
protected InputStream getRequestInputStream() throws IOException {
return new MessageInputStream(requestMessage, messageEncoding);
}
/*
* Sending
*/
@Override
protected void onSendBeforeWrite(WebServiceMessage message) throws IOException {
responseMessage = new Message(requestMessage.getFrom(), Message.Type.chat);
responseMessage.setFrom(connection.getUser());
responseMessage.setThread(requestMessage.getThread());
}
@Override
protected void addResponseHeader(String name, String value) throws IOException {
XmppTransportUtils.addHeader(responseMessage, name, value);
}
@Override
protected OutputStream getResponseOutputStream() throws IOException {
return new MessageOutputStream(responseMessage, messageEncoding);
}
@Override
protected void onSendAfterWrite(WebServiceMessage message) throws IOException {
connection.sendPacket(responseMessage);
}
}

View File

@@ -0,0 +1,177 @@
/*
* Copyright 2005-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.ws.transport.xmpp;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Iterator;
import org.springframework.util.Assert;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.transport.AbstractSenderConnection;
import org.springframework.ws.transport.xmpp.support.XmppTransportUtils;
import org.jivesoftware.smack.PacketCollector;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.filter.ThreadFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
/**
* Implementation of {@link org.springframework.ws.transport.WebServiceConnection} that is used for client-side XMPP
* access. Exposes a {@link Message} request and response message.
*
* @author Gildas Cuisinier
* @author Arjen Poutsma
* @since 2.0
*/
public class XmppSenderConnection extends AbstractSenderConnection {
private final Message requestMessage;
private final XMPPConnection connection;
private Message responseMessage;
private String messageEncoding;
private long receiveTimeout;
protected XmppSenderConnection(XMPPConnection connection, String to, String thread) {
Assert.notNull(connection, "'connection' must not be null");
Assert.hasLength(to, "'to' must not be empty");
Assert.hasLength(thread, "'thread' must not be empty");
this.connection = connection;
this.requestMessage = new Message(to, Message.Type.chat);
this.requestMessage.setThread(thread);
}
/** Returns the request message for this connection. */
public Message getRequestMessage() {
return requestMessage;
}
/** Returns the response message, if any, for this connection. */
public Message getResponseMessage() {
return responseMessage;
}
/*
* Package-friendly setters
*/
void setMessageEncoding(String messageEncoding) {
this.messageEncoding = messageEncoding;
}
void setReceiveTimeout(long receiveTimeout) {
this.receiveTimeout = receiveTimeout;
}
/*
* URI
*/
public URI getUri() throws URISyntaxException {
return XmppTransportUtils.toUri(requestMessage);
}
/*
* Errors
*/
public boolean hasError() {
return XmppTransportUtils.hasError(responseMessage);
}
public String getErrorMessage() {
return XmppTransportUtils.getErrorMessage(responseMessage);
}
/*
* Sending
*/
@Override
protected void addRequestHeader(String name, String value) {
XmppTransportUtils.addHeader(requestMessage, name, value);
}
@Override
protected OutputStream getRequestOutputStream() throws IOException {
return new MessageOutputStream(requestMessage, messageEncoding);
}
@Override
protected void onSendAfterWrite(WebServiceMessage message) throws IOException {
requestMessage.setFrom(connection.getUser());
connection.sendPacket(requestMessage);
}
/*
* Receiving
*/
@Override
protected void onReceiveBeforeRead() throws IOException {
PacketFilter packetFilter = createPacketFilter();
PacketCollector collector = connection.createPacketCollector(packetFilter);
Packet packet = receiveTimeout >= 0 ? collector.nextResult(receiveTimeout) : collector.nextResult();
if (packet instanceof Message) {
responseMessage = (Message) packet;
}
else if (packet != null) {
throw new IllegalArgumentException(
"Wrong packet type: [" + packet.getClass() + "]. Only Messages can be handled.");
}
}
private PacketFilter createPacketFilter() {
AndFilter andFilter = new AndFilter();
andFilter.addFilter(new PacketTypeFilter(Message.class));
andFilter.addFilter(new ThreadFilter(requestMessage.getThread()));
return andFilter;
}
@Override
protected boolean hasResponse() throws IOException {
return responseMessage != null;
}
@Override
protected Iterator<String> getResponseHeaderNames() {
return XmppTransportUtils.getHeaderNames(responseMessage);
}
@Override
protected Iterator<String> getResponseHeaders(String name) throws IOException {
return XmppTransportUtils.getHeaders(responseMessage, name);
}
@Override
protected InputStream getResponseInputStream() throws IOException {
return new MessageInputStream(responseMessage, messageEncoding);
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2005-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.ws.transport.xmpp;
import org.springframework.ws.transport.TransportConstants;
/**
* Declares XMPP-specific transport constants.
*
* @author Arjen Poutsma
* @since 2.0
*/
public interface XmppTransportConstants extends TransportConstants {
/**
* The "xmpp" URI scheme.
*/
String XMPP_URI_SCHEME = "xmpp";
}

View File

@@ -0,0 +1,5 @@
<html>
<body>
Package providing support for handling messages via xmpp.
</body>
</html>

View File

@@ -0,0 +1,136 @@
/*
* Copyright 2005-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.ws.transport.xmpp.support;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
/**
* Factory to make {@link org.jivesoftware.smack.XMPPConnection} and perform connection and login on the XMPP server
*
* @author Gildas Cuisinier
* @author Arjen Poutsma
* @since 2.0
*/
public class XmppConnectionFactoryBean implements FactoryBean<XMPPConnection>, InitializingBean, DisposableBean {
private static final int DEFAULT_PORT = 5222;
private XMPPConnection connection;
private String host;
private int port = DEFAULT_PORT;
private String serviceName;
private String username;
private String password;
private String resource;
/** Sets the server host to connect to. */
public void setHost(String host) {
this.host = host;
}
/**
* Sets the the server port to connect to.
* <p/>
* Defaults to {@code 5222}.
*/
public void setPort(int port) {
Assert.isTrue(port > 0, "'port' must be larger than 0");
this.port = port;
}
/** Sets the service name to connect to. */
public void setServiceName(String serviceName) {
this.serviceName = serviceName;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setResource(String resource) {
this.resource = resource;
}
public void afterPropertiesSet() throws XMPPException {
ConnectionConfiguration configuration = createConnectionConfiguration(host, port, serviceName);
Assert.notNull(configuration, "'configuration' must not be null");
Assert.hasText(username, "'username' must not be empty");
Assert.hasText(password, "'password' must not be empty");
connection = new XMPPConnection(configuration);
connection.connect();
if (StringUtils.hasText(resource)) {
connection.login(username, password, resource);
}
else {
connection.login(username, password);
}
}
public void destroy() {
connection.disconnect();
}
public XMPPConnection getObject() {
return connection;
}
public Class<XMPPConnection> getObjectType() {
return XMPPConnection.class;
}
public boolean isSingleton() {
return true;
}
/**
* Creates the {@code ConnectionConfiguration} from the given parameters.
*
* @param host the host to connect to
* @param port the port to connect to
* @param serviceName the name of the service to connect to. May be {@code null}
*/
protected ConnectionConfiguration createConnectionConfiguration(String host, int port, String serviceName) {
Assert.hasText(host, "'host' must not be empty");
if (StringUtils.hasText(serviceName)) {
return new ConnectionConfiguration(host, port, serviceName);
}
else {
return new ConnectionConfiguration(host, port);
}
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2005-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.ws.transport.xmpp.support;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.Iterator;
import org.springframework.util.Assert;
import org.springframework.ws.transport.xmpp.XmppTransportConstants;
import org.jivesoftware.smack.packet.Message;
/**
* Collection of utility methods to work with Mail transports.
*
* @author Arjen Poutsma
* @since 2.0
*/
public abstract class XmppTransportUtils {
private XmppTransportUtils() {
}
/**
* Converts the given XMPP destination into a <code>xmpp</code> URI.
*/
public static URI toUri(Message requestMessage) throws URISyntaxException {
return new URI(XmppTransportConstants.XMPP_URI_SCHEME, requestMessage.getTo(), null);
}
public static String getTo(URI uri) {
return uri.getSchemeSpecificPart();
}
public static boolean hasError(Message message) {
return message != null && Message.Type.error.equals(message.getType());
}
public static String getErrorMessage(Message message) {
if (message == null || !Message.Type.error.equals(message.getType())) {
return null;
}
else {
return message.getBody();
}
}
public static void addHeader(Message message, String name, String value) {
message.setProperty(name, value);
}
public static Iterator<String> getHeaderNames(Message message) {
Assert.notNull(message, "'message' must not be null");
return message.getPropertyNames().iterator();
}
public static Iterator<String> getHeaders(Message message, String name) {
Assert.notNull(message, "'message' must not be null");
String value = message.getProperty(name).toString();
if (value != null) {
return Collections.singletonList(value).iterator();
}
else {
return Collections.<String>emptyList().iterator();
}
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright 2005-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.ws.transport.xmpp.support;
import org.jivesoftware.smack.XMPPException;
import org.junit.Before;
import org.junit.Test;
/** @author Arjen Poutsma */
public class XmppConnectionFactoryBeanTest {
private XmppConnectionFactoryBean factoryBean;
@Before
public void createFactoryBean() {
factoryBean = new XmppConnectionFactoryBean();
}
@Test(expected = IllegalArgumentException.class)
public void noHost() throws XMPPException {
factoryBean.afterPropertiesSet();
}
@Test(expected = IllegalArgumentException.class)
public void noUsername() throws XMPPException {
factoryBean.setHost("jabber.org");
factoryBean.afterPropertiesSet();
}
@Test(expected = IllegalArgumentException.class)
public void wrongPort() throws XMPPException {
factoryBean.setPort(-10);
}
@Test
public void xs4all() throws XMPPException {
factoryBean.setHost("jabber.xs4all.nl");
factoryBean.setUsername("poutsma");
factoryBean.setPassword("tusega");
factoryBean.afterPropertiesSet();
factoryBean.destroy();
}
}