SWS-428 - XMPP support
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
Package providing support for handling messages via xmpp.
|
||||
</body>
|
||||
</html>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user