diff --git a/sandbox/src/main/java/org/springframework/ws/transport/mail/DefaultMonitoringStrategy.java b/sandbox/src/main/java/org/springframework/ws/transport/mail/DefaultMonitoringStrategy.java
deleted file mode 100644
index e98d6ac8..00000000
--- a/sandbox/src/main/java/org/springframework/ws/transport/mail/DefaultMonitoringStrategy.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright 2007 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.mail;
-
-import javax.mail.FetchProfile;
-import javax.mail.Flags;
-import javax.mail.Folder;
-import javax.mail.Message;
-import javax.mail.MessagingException;
-import javax.mail.search.AndTerm;
-import javax.mail.search.FlagTerm;
-import javax.mail.search.SearchTerm;
-
-/**
- * Default implementation of the {@link MonitoringStrategy}. Polls for new messages using a defined {@link
- * #setPollingInterval(int) interval}.
- *
- * @author Arjen Poutsma
- */
-public class DefaultMonitoringStrategy extends AbstractPollingMonitoringStrategy {
-
- private boolean deleteMessages = true;
-
- /**
- * Sets whether messages should be marked as {@link Flags.Flag#DELETED DELETED} after they have been read. Default
- * is true.
- */
- public void setDeleteMessages(boolean deleteMessages) {
- this.deleteMessages = deleteMessages;
- }
-
- /**
- * Polls for new messages in the given folder. Calls {@link #createSearchTerm(Folder)}, and uses that created term
- * to search for messages in the given folder. Marks the messages as {@link Flags.Flag#DELETED DELETED} if the
- * {@link #setDeleteMessages(boolean) deleteMessages} property is set.
- */
- protected final Message[] pollForNewMessages(Folder folder) throws MessagingException {
- SearchTerm searchTerm = createSearchTerm(folder);
- Message[] messages;
- if (searchTerm == null) {
- messages = folder.getMessages();
- }
- else {
- messages = folder.search(searchTerm);
- }
- if (messages.length > 0) {
- FetchProfile contentsProfile = new FetchProfile();
- contentsProfile.add(FetchProfile.Item.ENVELOPE);
- contentsProfile.add(FetchProfile.Item.CONTENT_INFO);
- folder.fetch(messages, contentsProfile);
- if (deleteMessages) {
- for (int i = 0; i < messages.length; i++) {
- messages[i].setFlag(Flags.Flag.DELETED, true);
- }
- }
- }
- return messages;
- }
-
- /**
- * Creates the search term that defines the messages to look for. Default implementation returns a term that
- * searches for all messages in the folder that are {@link Flags.Flag#RECENT RECENT}, not {@link Flags.Flag#ANSWERED
- * ANSWERED}, and not {@link Flags.Flag#DELETED DELETED}.
- *
null if all messages should be returned from {@link #pollForNewMessages(Folder)}.
- */
- protected SearchTerm createSearchTerm(Folder folder) {
- Flags supportedFlags = folder.getPermanentFlags();
- SearchTerm searchTerm = null;
- if (supportedFlags.contains(Flags.Flag.RECENT)) {
- searchTerm = new FlagTerm(new Flags(Flags.Flag.RECENT), true);
- }
- if (supportedFlags.contains(Flags.Flag.ANSWERED)) {
- FlagTerm answeredTerm = new FlagTerm(new Flags(Flags.Flag.ANSWERED), false);
- if (searchTerm == null) {
- searchTerm = answeredTerm;
- }
- else {
- searchTerm = new AndTerm(searchTerm, answeredTerm);
- }
- }
- if (supportedFlags.contains(Flags.Flag.DELETED)) {
- FlagTerm deletedTerm = new FlagTerm(new Flags(Flags.Flag.DELETED), false);
- if (searchTerm == null) {
- searchTerm = deletedTerm;
- }
- else {
- searchTerm = new AndTerm(searchTerm, deletedTerm);
- }
- }
- return searchTerm;
- }
-
-}
diff --git a/sandbox/src/main/java/org/springframework/ws/transport/mail/MailtoUri.java b/sandbox/src/main/java/org/springframework/ws/transport/mail/MailtoUri.java
deleted file mode 100644
index e997aae8..00000000
--- a/sandbox/src/main/java/org/springframework/ws/transport/mail/MailtoUri.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2007 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.mail;
-
-import javax.mail.internet.AddressException;
-import javax.mail.internet.InternetAddress;
-
-import org.springframework.util.Assert;
-import org.springframework.ws.transport.support.ParameterizedUri;
-
-/**
- * @author Arjen Poutsma
- */
-public class MailtoUri extends ParameterizedUri {
-
- public MailtoUri(String uri) {
- super(uri);
- Assert.isTrue(uri.startsWith(MailTransportConstants.URI_SCHEME), "Invalid uri: " + uri);
- try {
- InternetAddress.parse(getDestination(), false);
- }
- catch (AddressException ex) {
- throw new IllegalArgumentException(ex);
- }
- }
-
- public InternetAddress getTo() throws AddressException {
- return new InternetAddress(getDestination());
- }
-
- public String getSubject() {
- return getParameter("subject");
- }
-
- public boolean hasSubject() {
- return hasParameter("subject");
- }
-
- public boolean hasCc() {
- return hasParameter("cc");
- }
-
- public InternetAddress getCc() throws AddressException {
- return new InternetAddress(getParameter("cc"));
- }
-}
diff --git a/sandbox/src/main/java/org/springframework/ws/transport/support/ParameterizedUri.java b/sandbox/src/main/java/org/springframework/ws/transport/support/ParameterizedUri.java
deleted file mode 100644
index 58be7494..00000000
--- a/sandbox/src/main/java/org/springframework/ws/transport/support/ParameterizedUri.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright 2007 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.support;
-
-import java.util.Map;
-import java.util.StringTokenizer;
-
-import org.springframework.core.CollectionFactory;
-import org.springframework.util.Assert;
-
-/** @author Arjen Poutsma */
-public class ParameterizedUri {
-
- private final String uri;
-
- private final String scheme;
-
- // keys are string parameter names; values are string parameter values
- private final Map parameters = CollectionFactory.createLinkedCaseInsensitiveMapIfPossible(5);
-
- private final String destination;
-
- public ParameterizedUri(String uri) {
- Assert.hasLength(uri, "'uri' must not be empty");
- this.uri = uri;
- int scIdx = uri.indexOf(':');
- Assert.isTrue(scIdx != -1, uri + " does contain scheme");
- scheme = uri.substring(0, scIdx);
- Assert.isTrue(uri.length() > scheme.length(), uri + " does not have a destination");
- int paramStart = uri.indexOf('?');
- if (paramStart == -1) {
- destination = uri.substring(scIdx + 1);
- }
- else {
- destination = uri.substring(scIdx + 1, paramStart);
- parseParameters(uri.substring(paramStart + 1));
- }
- }
-
- private void parseParameters(String parametersString) {
- StringTokenizer params = new StringTokenizer(parametersString, "&");
- while (params.hasMoreTokens()) {
- String param = params.nextToken();
- int paramSep = param.indexOf('=');
- if (paramSep == -1) {
- throw new IllegalArgumentException(param + " is not a valid parameter: it has no '='");
- }
- String paramName = param.substring(0, paramSep);
- String paramValue = param.substring(paramSep + 1);
- parameters.put(paramName, paramValue);
- }
- }
-
- /** Returns the destination of the uri. */
- protected String getDestination() {
- return destination;
- }
-
- public String toString() {
- return uri;
- }
-
- protected String getParameter(String paramName) {
- return (String) parameters.get(paramName);
- }
-
- protected boolean hasParameter(String paramName) {
- return parameters.containsKey(paramName);
- }
-}
diff --git a/sandbox/src/main/java/org/springframework/ws/transport/support/SimpleWebServiceMessageReceiverObjectSupport.java b/sandbox/src/main/java/org/springframework/ws/transport/support/SimpleWebServiceMessageReceiverObjectSupport.java
deleted file mode 100644
index eeaec2f9..00000000
--- a/sandbox/src/main/java/org/springframework/ws/transport/support/SimpleWebServiceMessageReceiverObjectSupport.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2007 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.support;
-
-import org.springframework.beans.factory.InitializingBean;
-import org.springframework.util.Assert;
-import org.springframework.ws.transport.WebServiceConnection;
-import org.springframework.ws.transport.WebServiceMessageReceiver;
-
-/**
- * Base class for server-side transport objects which have a predefined {@link WebServiceMessageReceiver}.
- *
- * @author Arjen Poutsma
- * @see #handleConnection(WebServiceConnection)
- * @since 1.1.0
- */
-public abstract class SimpleWebServiceMessageReceiverObjectSupport extends WebServiceMessageReceiverObjectSupport
- implements InitializingBean {
-
- private WebServiceMessageReceiver messageReceiver;
-
- /**
- * Returns the WebServiceMessageReceiver used by this listener.
- */
- public WebServiceMessageReceiver getMessageReceiver() {
- return messageReceiver;
- }
-
- /**
- * Sets the WebServiceMessageReceiver used by this listener.
- */
- public void setMessageReceiver(WebServiceMessageReceiver messageReceiver) {
- this.messageReceiver = messageReceiver;
- }
-
- public void afterPropertiesSet() throws Exception {
- super.afterPropertiesSet();
- Assert.notNull(getMessageReceiver(), "messageReceiver must not be null");
- }
-
- protected final void handleConnection(WebServiceConnection connection) throws Exception {
- handleConnection(connection, getMessageReceiver());
- }
-
-}
diff --git a/sandbox/src/test/java/org/springframework/ws/transport/SimpleTestingMessageReceiver.java b/sandbox/src/test/java/org/springframework/ws/transport/SimpleTestingMessageReceiver.java
deleted file mode 100644
index aff2af56..00000000
--- a/sandbox/src/test/java/org/springframework/ws/transport/SimpleTestingMessageReceiver.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2007 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;
-
-import javax.xml.transform.Transformer;
-
-import org.springframework.ws.context.MessageContext;
-import org.springframework.xml.transform.TransformerObjectSupport;
-import org.springframework.util.Assert;
-
-public class SimpleTestingMessageReceiver extends TransformerObjectSupport implements WebServiceMessageReceiver {
-
- public void receive(MessageContext messageContext) throws Exception {
- Assert.notNull(messageContext, "MessageContext is null");
- logger.info("Received message");
- Transformer transformer = createTransformer();
- transformer.transform(messageContext.getRequest().getPayloadSource(),
- messageContext.getResponse().getPayloadResult());
- }
-}
diff --git a/sandbox/src/test/java/org/springframework/ws/transport/mail/Driver.java b/sandbox/src/test/java/org/springframework/ws/transport/mail/Driver.java
deleted file mode 100644
index 36aa86e2..00000000
--- a/sandbox/src/test/java/org/springframework/ws/transport/mail/Driver.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2007 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.mail;
-
-import java.io.IOException;
-
-import org.springframework.context.support.ClassPathXmlApplicationContext;
-
-/**
- * @author Arjen Poutsma
- */
-public class Driver {
-
- public static void main(String[] args) throws IOException {
- ClassPathXmlApplicationContext context =
- new ClassPathXmlApplicationContext("applicationContext.xml", Driver.class);
- context.registerShutdownHook();
- System.out.println("Started....");
- System.in.read();
- }
-
-}
diff --git a/sandbox/src/test/java/org/springframework/ws/transport/mail/MailMessageSenderIntegrationTest.java b/sandbox/src/test/java/org/springframework/ws/transport/mail/MailMessageSenderIntegrationTest.java
deleted file mode 100644
index e841a249..00000000
--- a/sandbox/src/test/java/org/springframework/ws/transport/mail/MailMessageSenderIntegrationTest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2007 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.mail;
-
-import javax.xml.namespace.QName;
-import javax.xml.soap.MessageFactory;
-import javax.xml.soap.SOAPConstants;
-import javax.xml.soap.SOAPMessage;
-
-import junit.framework.TestCase;
-import org.springframework.ws.soap.SoapMessage;
-import org.springframework.ws.soap.saaj.SaajSoapMessage;
-import org.springframework.ws.transport.WebServiceConnection;
-
-public class MailMessageSenderIntegrationTest extends TestCase {
-
- private MailMessageSender messageSender;
-
- private MessageFactory messageFactory;
-
- private static final String URI = "mailto:ajwpi21@xs4all.nl?subject=SOAP Test";
-// private static final String URI = "mailto:revans@interface21.com?subject=Believe me now?";
-
- private static final String SOAP_ACTION = "http://springframework.org/DoIt";
-
- protected void setUp() throws Exception {
- messageSender = new MailMessageSender();
- messageSender.setFrom("Arjen Poutsma monitoringStrategy
+ * is not explicitly set, this receiver will use the {@link Pop3PollingMonitoringStrategy} for POP3 servers, and the
+ * {@link PollingMonitoringStrategy} for IMAP servers.
+ *
+ * @author Arjen Poutsma
+ * @since 1.1.0
+ */
+public class MailMessageReceiver extends AbstractAsyncStandaloneMessageReceiver {
+
+ private Session session = Session.getInstance(new Properties(), null);
+
+ private URLName storeUri;
+
+ private URLName transportUri;
+
+ private Folder folder;
+
+ private Store store;
+
+ private InternetAddress from;
+
+ private MonitoringStrategy monitoringStrategy;
+
+ /** Sets the from address to use when sending reponse messages. */
+ public void setFrom(String from) throws AddressException {
+ this.from = new InternetAddress(from);
+ }
+
+ /**
+ * Set JavaMail properties for the {@link Session}.
+ *
+ * A new {@link Session} will be created with those properties. Use either this method or {@link #setSession}, but
+ * not both.
+ *
+ * Non-default properties in this instance will override given JavaMail properties.
+ */
+ public void setJavaMailProperties(Properties javaMailProperties) {
+ session = Session.getInstance(javaMailProperties, null);
+ }
+
+ /**
+ * Set the JavaMail Session, possibly pulled from JNDI.
+ *
+ * Default is a new Session without defaults, that is completely configured via this instance's
+ * properties.
+ *
+ * If using a pre-configured Session, non-default properties in this instance will override the
+ * settings in the Session.
+ *
+ * @see #setJavaMailProperties
+ */
+ public void setSession(Session session) {
+ Assert.notNull(session, "Session must not be null");
+ this.session = session;
+ }
+
+ /**
+ * Sets the JavaMail Store URI to be used for retrieving request messages. Typically takes the form of
+ * [imap|pop3]://user:password@host:port/INBOX. Setting this property is required.
+ *
+ * For example, imap://john:secret@imap.example.com/INBOX
+ *
+ * @see Session#getStore(URLName)
+ */
+ public void setStoreUri(String storeUri) {
+ this.storeUri = new URLName(storeUri);
+ }
+
+ /**
+ * Sets the JavaMail Transport URI to be used for sending response messages. Typically takes the form of
+ * smtp://user:password@host:port. Setting this property is required.
+ *
+ * For example, smtp://john:secret@smtp.example.com
+ *
+ * @see Session#getTransport(URLName)
+ */
+ public void setTransportUri(String transportUri) {
+ this.transportUri = new URLName(transportUri);
+ }
+
+ /**
+ * Sets the monitoring strategy to use for retrieving new requests. Default is the {@link
+ * PollingMonitoringStrategy}.
+ */
+ public void setMonitoringStrategy(MonitoringStrategy monitoringStrategy) {
+ this.monitoringStrategy = monitoringStrategy;
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ Assert.notNull(storeUri, "Property 'storeUri' is required");
+ Assert.notNull(transportUri, "Property 'transportUri' is required");
+ if (monitoringStrategy == null) {
+ String protocol = storeUri.getProtocol();
+ if ("pop3".equals(protocol)) {
+ monitoringStrategy = new Pop3PollingMonitoringStrategy();
+ }
+ else if ("imap".equals(protocol)) {
+ monitoringStrategy = new PollingMonitoringStrategy();
+ }
+ else {
+ throw new IllegalArgumentException("Cannot determine monitoring strategy for \"" + protocol + "\". " +
+ "Set the 'monitoringStrategy' explicitly.");
+ }
+ }
+ super.afterPropertiesSet();
+ }
+
+ protected void onActivate() throws MessagingException {
+ openSession();
+ openFolder();
+ }
+
+ protected void onStart() {
+ if (logger.isInfoEnabled()) {
+ logger.info("Starting mail receiver [" + MailTransportUtils.toPasswordProtectedString(storeUri) + "]");
+ }
+ execute(new MonitoringRunnable());
+ }
+
+ protected void onStop() {
+ if (logger.isInfoEnabled()) {
+ logger.info("Stopping mail receiver [" + MailTransportUtils.toPasswordProtectedString(storeUri) + "]");
+ }
+ closeFolder();
+ }
+
+ protected void onShutdown() {
+ if (logger.isInfoEnabled()) {
+ logger.info("Shutting down mail receiver [" + MailTransportUtils.toPasswordProtectedString(storeUri) + "]");
+ }
+ closeFolder();
+ closeSession();
+ }
+
+ private void openSession() throws MessagingException {
+ store = session.getStore(storeUri);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Connecting to store [" + MailTransportUtils.toPasswordProtectedString(storeUri) + "]");
+ }
+ store.connect();
+ }
+
+ private void openFolder() throws MessagingException {
+ if (folder != null && folder.isOpen()) {
+ return;
+ }
+ folder = store.getFolder(storeUri);
+ if (folder == null || !folder.exists()) {
+ throw new IllegalStateException("No default folder to receive from");
+ }
+ if (logger.isDebugEnabled()) {
+ logger.debug("Opening folder [" + MailTransportUtils.toPasswordProtectedString(storeUri) + "]");
+ }
+ folder.open(monitoringStrategy.getFolderOpenMode());
+ }
+
+ private void closeFolder() {
+ MailTransportUtils.closeFolder(folder, true);
+ }
+
+ private void closeSession() {
+ MailTransportUtils.closeService(store);
+ }
+
+ private class MonitoringRunnable implements SchedulingAwareRunnable {
+
+ public void run() {
+ try {
+ openFolder();
+ while (isRunning()) {
+ try {
+ Message[] messages = monitoringStrategy.monitor(folder);
+ for (int i = 0; i < messages.length; i++) {
+ if (logger.isDebugEnabled()) {
+ if (messages[i] instanceof MimeMessage) {
+ MimeMessage mimeMessage = (MimeMessage) messages[i];
+ logger.debug("Received email message with MessageID " + mimeMessage.getMessageID());
+ }
+ }
+ MessageHandler handler = new MessageHandler(messages[i]);
+ execute(handler);
+ }
+ }
+ catch (FolderClosedException ex) {
+ logger.debug("Folder closed, reopening");
+ if (isRunning()) {
+ openFolder();
+ }
+ }
+ catch (MessagingException ex) {
+ logger.warn(ex);
+ }
+ }
+ }
+ catch (InterruptedException ex) {
+ // Restore the interrupted status
+ Thread.currentThread().interrupt();
+ }
+ catch (MessagingException ex) {
+ logger.error(ex);
+ }
+ }
+
+ public boolean isLongLived() {
+ return true;
+ }
+ }
+
+ private class MessageHandler implements SchedulingAwareRunnable {
+
+ private final Message message;
+
+ public MessageHandler(Message message) {
+ this.message = message;
+ }
+
+ public void run() {
+ MailReceiverConnection connection = new MailReceiverConnection(message, session);
+ connection.setTransportUri(transportUri);
+ connection.setFrom(from);
+ try {
+ handleConnection(connection);
+ }
+ catch (Exception ex) {
+ logger.error("Could not handle incoming mail connection", ex);
+ }
+ }
+
+ public boolean isLongLived() {
+ return false;
+ }
+ }
+
+}
diff --git a/support/src/main/java/org/springframework/ws/transport/mail/MailMessageSender.java b/support/src/main/java/org/springframework/ws/transport/mail/MailMessageSender.java
new file mode 100644
index 00000000..3506c256
--- /dev/null
+++ b/support/src/main/java/org/springframework/ws/transport/mail/MailMessageSender.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2007 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.mail;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.Properties;
+import javax.mail.Session;
+import javax.mail.URLName;
+import javax.mail.internet.AddressException;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.util.Assert;
+import org.springframework.ws.WebServiceMessageFactory;
+import org.springframework.ws.transport.WebServiceConnection;
+import org.springframework.ws.transport.WebServiceMessageSender;
+import org.springframework.ws.transport.mail.monitor.PollingMonitoringStrategy;
+import org.springframework.ws.transport.mail.support.MailTransportUtils;
+
+/**
+ * {@link WebServiceMessageSender} implementation that uses Mail {@link MimeMessage}s. Requires a {@link
+ * #setTransportUri(String) transport} and {@link #setStoreUri(String) store} URI to be set.
+ *
+ * Calling {@link WebServiceConnection#receive(WebServiceMessageFactory)} on connections created by this message sender
+ * will result in a blocking call, for the amount of milliseconds specified by the {@link #setReceiveSleepTime(long)
+ * receiveSleepTime} property. This will give the server time to formulate a response message. By default, this propery
+ * is set to 1 minute. For a proper request-response conversation to work, this property value must not be smaller the
+ * {@link PollingMonitoringStrategy#setPollingInterval(long) pollingInterval} property of the server-side message
+ * receiver polling strategy.
+ *
+ * This message sender supports URI's of the following format: mailto:to[?param-name=param-value][¶m-name=param-value]* + *where the characters :, ?, and & stand for + * themselves. The to represents a RFC 822 mailbox. Valid param-name include: + * + *
+ * + * Some examples of email URIs are: + * + *
+ * param-name Description subject The subject of the request message.
mailto:john@example.com+ * + * @author Arjen Poutsma + * @see The mailto URL scheme + * @since 1.1.0 + */ +public class MailMessageSender implements WebServiceMessageSender, InitializingBean { + + /** Default timeout for receive operations. Set to 1000 * 60 milliseconds (i.e. 1 minute). */ + public static final long DEFAULT_RECEIVE_TIMEOUT = 1000 * 60; + + private long receiveSleepTime = DEFAULT_RECEIVE_TIMEOUT; + + private Session session = Session.getInstance(new Properties(), null); + + private URLName storeUri; + + private URLName transportUri; + + private InternetAddress from; + + /** Sets the from address to use when sending request messages. */ + public void setFrom(String from) throws AddressException { + this.from = new InternetAddress(from); + } + + /** + * Set JavaMail properties for the {@link Session}. + * + * A new {@link Session} will be created with those properties. Use either this method or {@link #setSession}, but + * not both. + * + * Non-default properties in this instance will override given JavaMail properties. + */ + public void setJavaMailProperties(Properties javaMailProperties) { + session = Session.getInstance(javaMailProperties, null); + } + + /** + * Set the sleep time to use for receive calls, in milliseconds. The default is 1000 * 60 ms, that + * is 1 minute. + */ + public void setReceiveSleepTime(long receiveSleepTime) { + this.receiveSleepTime = receiveSleepTime; + } + + /** + * Set the JavaMail
mailto:john@example.com@?subject=SOAP%20Test
Session, possibly pulled from JNDI.
+ *
+ * Default is a new Session without defaults, that is completely configured via this instance's
+ * properties.
+ *
+ * If using a pre-configured Session, non-default properties in this instance will override the
+ * settings in the Session.
+ *
+ * @see #setJavaMailProperties
+ */
+ public void setSession(Session session) {
+ Assert.notNull(session, "Session must not be null");
+ this.session = session;
+ }
+
+ /**
+ * Sets the JavaMail Store URI to be used for retrieving response messages. Typically takes the form of
+ * [imap|pop3]://user:password@host:port/INBOX. Setting this property is required.
+ *
+ * For example, imap://john:secret@imap.example.com/INBOX
+ *
+ * @see Session#getStore(URLName)
+ */
+ public void setStoreUri(String storeUri) {
+ this.storeUri = new URLName(storeUri);
+ }
+
+ /**
+ * Sets the JavaMail Transport URI to be used for sending response messages. Typically takes the form of
+ * smtp://user:password@host:port. Setting this property is required.
+ *
+ * For example, smtp://john:secret@smtp.example.com
+ *
+ * @see Session#getTransport(URLName)
+ */
+ public void setTransportUri(String transportUri) {
+ this.transportUri = new URLName(transportUri);
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ Assert.notNull(transportUri, "'transportUri' is required");
+ Assert.notNull(storeUri, "'storeUri' is required");
+ }
+
+ public WebServiceConnection createConnection(URI uri) throws IOException {
+ InternetAddress to = MailTransportUtils.getTo(uri);
+ MailSenderConnection connection =
+ new MailSenderConnection(session, transportUri, storeUri, to, receiveSleepTime);
+ if (from != null) {
+ connection.setFrom(from);
+ }
+ String subject = MailTransportUtils.getSubject(uri);
+ if (subject != null) {
+ connection.setSubject(subject);
+ }
+ return connection;
+ }
+
+ public boolean supports(URI uri) {
+ return uri.getScheme().equals(MailTransportConstants.MAIL_URI_SCHEME);
+ }
+}
diff --git a/sandbox/src/main/java/org/springframework/ws/transport/mail/MailReceiverConnection.java b/support/src/main/java/org/springframework/ws/transport/mail/MailReceiverConnection.java
similarity index 85%
rename from sandbox/src/main/java/org/springframework/ws/transport/mail/MailReceiverConnection.java
rename to support/src/main/java/org/springframework/ws/transport/mail/MailReceiverConnection.java
index e4a63947..619ddab7 100644
--- a/sandbox/src/main/java/org/springframework/ws/transport/mail/MailReceiverConnection.java
+++ b/support/src/main/java/org/springframework/ws/transport/mail/MailReceiverConnection.java
@@ -40,10 +40,15 @@ import org.springframework.util.Assert;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.transport.AbstractReceiverConnection;
import org.springframework.ws.transport.TransportConstants;
-import org.springframework.ws.transport.mail.support.MailUtils;
+import org.springframework.ws.transport.WebServiceConnection;
+import org.springframework.ws.transport.mail.support.MailTransportUtils;
/**
+ * Implementation of {@link WebServiceConnection} that is used for server-side Mail access. Exposes a {@link Message}
+ * request and response message.
+ *
* @author Arjen Poutsma
+ * @since 1.1.0
*/
public class MailReceiverConnection extends AbstractReceiverConnection {
@@ -61,13 +66,39 @@ public class MailReceiverConnection extends AbstractReceiverConnection {
private InternetAddress from;
- public MailReceiverConnection(Message requestMessage, Session session) {
+ /** Constructs a new Mail connection with the given parameters. */
+ protected MailReceiverConnection(Message requestMessage, Session session) {
Assert.notNull(requestMessage, "'requestMessage' must not be null");
Assert.notNull(session, "'session' must not be null");
this.requestMessage = requestMessage;
this.session = session;
}
+ /** 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 setTransportUri(URLName transportUri) {
+ this.transportUri = transportUri;
+ }
+
+ void setFrom(InternetAddress from) {
+ this.from = from;
+ }
+
+ /*
+ * Errors
+ */
+
public String getErrorMessage() throws IOException {
return null;
}
@@ -76,10 +107,6 @@ public class MailReceiverConnection extends AbstractReceiverConnection {
return false;
}
- public void setTransportUri(URLName transportUri) {
- this.transportUri = transportUri;
- }
-
public void close() throws IOException {
}
@@ -167,14 +194,10 @@ public class MailReceiverConnection extends AbstractReceiverConnection {
throw new MailTransportException(ex);
}
finally {
- MailUtils.closeService(transport);
+ MailTransportUtils.closeService(transport);
}
}
- public void setFrom(InternetAddress from) {
- this.from = from;
- }
-
private class ByteArrayDataSource implements DataSource {
private byte[] data;
diff --git a/support/src/main/java/org/springframework/ws/transport/mail/MailSenderConnection.java b/support/src/main/java/org/springframework/ws/transport/mail/MailSenderConnection.java
new file mode 100644
index 00000000..85c1d8a9
--- /dev/null
+++ b/support/src/main/java/org/springframework/ws/transport/mail/MailSenderConnection.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright 2007 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.mail;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import javax.activation.DataHandler;
+import javax.activation.DataSource;
+import javax.mail.Flags;
+import javax.mail.Folder;
+import javax.mail.Header;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.Store;
+import javax.mail.Transport;
+import javax.mail.URLName;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+import javax.mail.search.HeaderTerm;
+import javax.mail.search.SearchTerm;
+
+import org.springframework.util.Assert;
+import org.springframework.ws.WebServiceMessage;
+import org.springframework.ws.transport.AbstractSenderConnection;
+import org.springframework.ws.transport.TransportConstants;
+import org.springframework.ws.transport.WebServiceConnection;
+import org.springframework.ws.transport.mail.support.MailTransportUtils;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Implementation of {@link WebServiceConnection} that is used for client-side Mail access. Exposes a {@link Message}
+ * request and response message.
+ *
+ * @author Arjen Poutsma
+ * @since 1.1.0
+ */
+
+public class MailSenderConnection extends AbstractSenderConnection {
+
+ private static final Log logger = LogFactory.getLog(MailSenderConnection.class);
+
+ private final Session session;
+
+ private MimeMessage requestMessage;
+
+ private Message responseMessage;
+
+ private String requestContentType;
+
+ private boolean deleteAfterReceive = false;
+
+ private final URLName storeUri;
+
+ private final URLName transportUri;
+
+ private ByteArrayOutputStream requestBuffer;
+
+ private InternetAddress from;
+
+ private final InternetAddress to;
+
+ private String subject;
+
+ private final long receiveTimeout;
+
+ private Store store;
+
+ private Folder folder;
+
+ /** Constructs a new Mail connection with the given parameters. */
+ protected MailSenderConnection(Session session,
+ URLName transportUri,
+ URLName storeUri,
+ InternetAddress to,
+ long receiveTimeout) {
+ Assert.notNull(session, "'session' must not be null");
+ Assert.notNull(transportUri, "'transportUri' must not be null");
+ Assert.notNull(storeUri, "'storeUri' must not be null");
+ Assert.notNull(to, "'to' must not be null");
+ this.session = session;
+ this.transportUri = transportUri;
+ this.storeUri = storeUri;
+ this.to = to;
+ this.receiveTimeout = receiveTimeout;
+ }
+
+ /** 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 setFrom(InternetAddress from) {
+ this.from = from;
+ }
+
+ void setSubject(String subject) {
+ this.subject = subject;
+ }
+
+ /*
+ * Sending
+ */
+ protected void onSendBeforeWrite(WebServiceMessage message) throws IOException {
+ try {
+ requestMessage = new MimeMessage(session);
+ requestMessage.setRecipient(Message.RecipientType.TO, to);
+ requestMessage.setSentDate(new Date());
+ if (from != null) {
+ requestMessage.setFrom(from);
+ }
+ if (subject != null) {
+ requestMessage.setSubject(subject);
+ }
+ requestBuffer = new ByteArrayOutputStream();
+ }
+ catch (MessagingException ex) {
+ throw new MailTransportException(ex);
+ }
+ }
+
+ protected void addRequestHeader(String name, String value) throws IOException {
+ try {
+ requestMessage.addHeader(name, value);
+ if (TransportConstants.HEADER_CONTENT_TYPE.equals(name)) {
+ requestContentType = value;
+ }
+ }
+ catch (MessagingException ex) {
+ throw new MailTransportException(ex);
+ }
+ }
+
+ protected OutputStream getRequestOutputStream() throws IOException {
+ return requestBuffer;
+ }
+
+ protected void onSendAfterWrite(WebServiceMessage message) throws IOException {
+ Transport transport = null;
+ try {
+ requestMessage.setDataHandler(
+ new DataHandler(new ByteArrayDataSource(requestContentType, requestBuffer.toByteArray())));
+ transport = session.getTransport(transportUri);
+ transport.connect();
+ requestMessage.saveChanges();
+ transport.sendMessage(requestMessage, requestMessage.getAllRecipients());
+ }
+ catch (MessagingException ex) {
+ throw new MailTransportException(ex);
+ }
+ finally {
+ MailTransportUtils.closeService(transport);
+ }
+ }
+
+ /*
+ * Receiving
+ */
+
+ protected void onReceiveBeforeRead() throws IOException {
+ try {
+ String requestMessageId = requestMessage.getMessageID();
+ Assert.hasLength(requestMessageId, "No Message-ID found on request message [" + requestMessage + "]");
+ try {
+ Thread.sleep(receiveTimeout);
+ }
+ catch (InterruptedException e) {
+ // Re-interrupt current thread, to allow other threads to react.
+ Thread.currentThread().interrupt();
+ }
+ openFolder();
+ SearchTerm searchTerm = new HeaderTerm(MailTransportConstants.HEADER_IN_REPLY_TO, requestMessageId);
+ Message[] responses = folder.search(searchTerm);
+ if (responses.length > 0) {
+ if (responses.length > 1) {
+ logger.warn("Received more than one response for request with ID [" + requestMessageId + "]");
+ }
+ responseMessage = responses[0];
+ }
+ if (deleteAfterReceive) {
+ responseMessage.setFlag(Flags.Flag.DELETED, true);
+ }
+ }
+ catch (MessagingException ex) {
+ throw new MailTransportException(ex);
+ }
+ }
+
+ private void openFolder() throws MessagingException {
+ store = session.getStore(storeUri);
+ store.connect();
+ folder = store.getFolder(storeUri);
+ if (folder == null || !folder.exists()) {
+ throw new IllegalStateException("No default folder to receive from");
+ }
+ if (deleteAfterReceive) {
+ folder.open(Folder.READ_WRITE);
+ }
+ else {
+ folder.open(Folder.READ_ONLY);
+ }
+ }
+
+ protected boolean hasResponse() throws IOException {
+ return responseMessage != null;
+ }
+
+ protected Iterator getResponseHeaderNames() throws IOException {
+ try {
+ List headers = new ArrayList();
+ Enumeration enumeration = responseMessage.getAllHeaders();
+ while (enumeration.hasMoreElements()) {
+ Header header = (Header) enumeration.nextElement();
+ headers.add(header.getName());
+ }
+ return headers.iterator();
+ }
+ catch (MessagingException ex) {
+ throw new MailTransportException(ex);
+ }
+ }
+
+ protected Iterator getResponseHeaders(String name) throws IOException {
+ try {
+ String[] headers = responseMessage.getHeader(name);
+ return Arrays.asList(headers).iterator();
+ }
+ catch (MessagingException ex) {
+ throw new MailTransportException(ex);
+
+ }
+ }
+
+ protected InputStream getResponseInputStream() throws IOException {
+ try {
+ return responseMessage.getDataHandler().getInputStream();
+ }
+ catch (MessagingException ex) {
+ throw new MailTransportException(ex);
+ }
+ }
+
+ public boolean hasError() throws IOException {
+ return false;
+ }
+
+ public String getErrorMessage() throws IOException {
+ return null;
+ }
+
+ public void close() throws IOException {
+ MailTransportUtils.closeFolder(folder, deleteAfterReceive);
+ MailTransportUtils.closeService(store);
+ }
+
+ private class ByteArrayDataSource implements DataSource {
+
+ private byte[] data;
+
+ private String contentType;
+
+ public ByteArrayDataSource(String contentType, byte[] data) {
+ this.data = data;
+ this.contentType = contentType;
+ }
+
+ public InputStream getInputStream() throws IOException {
+ return new ByteArrayInputStream(data);
+ }
+
+ public OutputStream getOutputStream() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getContentType() {
+ return contentType;
+ }
+
+ public String getName() {
+ return "ByteArrayDataSource";
+ }
+ }
+
+}
diff --git a/sandbox/src/main/java/org/springframework/ws/transport/mail/MailTransportConstants.java b/support/src/main/java/org/springframework/ws/transport/mail/MailTransportConstants.java
similarity index 83%
rename from sandbox/src/main/java/org/springframework/ws/transport/mail/MailTransportConstants.java
rename to support/src/main/java/org/springframework/ws/transport/mail/MailTransportConstants.java
index 6d2c61b3..aa413ece 100644
--- a/sandbox/src/main/java/org/springframework/ws/transport/mail/MailTransportConstants.java
+++ b/support/src/main/java/org/springframework/ws/transport/mail/MailTransportConstants.java
@@ -19,14 +19,16 @@ package org.springframework.ws.transport.mail;
import org.springframework.ws.transport.TransportConstants;
/**
+ * Declares Mail-specific transport constants.
+ *
* @author Arjen Poutsma
+ * @since 1.1.0
*/
public interface MailTransportConstants extends TransportConstants {
- /**
- * The "In-Reply-To" header.
- */
- String HEADER_IN_REPLY_TO = "In-Reply-To";
+ /** The "mail" URI scheme. */
+ String MAIL_URI_SCHEME = "mailto";
- String URI_SCHEME = "mailto";
+ /** The "In-Reply-To" header. */
+ String HEADER_IN_REPLY_TO = "In-Reply-To";
}
diff --git a/support/src/main/java/org/springframework/ws/transport/mail/MailTransportException.java b/support/src/main/java/org/springframework/ws/transport/mail/MailTransportException.java
new file mode 100644
index 00000000..b166387d
--- /dev/null
+++ b/support/src/main/java/org/springframework/ws/transport/mail/MailTransportException.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2007 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.mail;
+
+import javax.mail.MessagingException;
+
+import org.springframework.ws.transport.TransportException;
+
+/**
+ * Exception that is thrown when an error occurs in the Mail transport.
+ *
+ * @author Arjen Poutsma
+ * @since 1.1.0
+ */
+public class MailTransportException extends TransportException {
+
+ private final MessagingException messagingException;
+
+ public MailTransportException(String msg, MessagingException ex) {
+ super(msg + ": " + ex.getMessage());
+ initCause(ex);
+ messagingException = ex;
+ }
+
+ public MailTransportException(MessagingException ex) {
+ super(ex.getMessage());
+ initCause(ex);
+ messagingException = ex;
+ }
+
+ public MessagingException getMessagingException() {
+ return messagingException;
+ }
+}
diff --git a/support/src/main/java/org/springframework/ws/transport/mail/monitor/AbstractMonitoringStrategy.java b/support/src/main/java/org/springframework/ws/transport/mail/monitor/AbstractMonitoringStrategy.java
new file mode 100644
index 00000000..96b8e22b
--- /dev/null
+++ b/support/src/main/java/org/springframework/ws/transport/mail/monitor/AbstractMonitoringStrategy.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2007 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.mail.monitor;
+
+import javax.mail.FetchProfile;
+import javax.mail.Flags;
+import javax.mail.Folder;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.search.AndTerm;
+import javax.mail.search.FlagTerm;
+import javax.mail.search.SearchTerm;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Abstract base class for the {@link MonitoringStrategy} interface. Exposes a {@link #setDeleteMessages(boolean)
+ * deleteMessages} property, and includes a basic workflow for message monitoring.
+ *
+ * @author Arjen Poutsma
+ * @since 1.1.0
+ */
+public abstract class AbstractMonitoringStrategy implements MonitoringStrategy {
+
+ /** Logger available to subclasses. */
+ protected final Log logger = LogFactory.getLog(getClass());
+
+ private boolean deleteMessages = true;
+
+ /**
+ * Sets whether messages should be marked as {@link Flags.Flag#DELETED DELETED} after they have been read. Default
+ * is true.
+ */
+ public void setDeleteMessages(boolean deleteMessages) {
+ this.deleteMessages = deleteMessages;
+ }
+
+ public int getFolderOpenMode() {
+ return deleteMessages ? Folder.READ_WRITE : Folder.READ_ONLY;
+ }
+
+ /**
+ * Monitors the given folder, and returns any new messages when they arrive. This implementation calls {@link
+ * #waitForNewMessages(Folder)}, then searches for new messages using {@link #searchForNewMessages(Folder)}, fetches
+ * the messages using {@link #fetchMessages(Folder, Message[])}, and finally {@link #setDeleteMessages(boolean)
+ * deletes} the messages, if {@link #setDeleteMessages(boolean) deleteMessages} is true.
+ *
+ * @param folder the folder to monitor
+ * @return the new messages
+ * @throws MessagingException in case of JavaMail errors
+ * @throws InterruptedException when a thread is interrupted
+ */
+ public final Message[] monitor(Folder folder) throws MessagingException, InterruptedException {
+ waitForNewMessages(folder);
+ Message[] messages = searchForNewMessages(folder);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Found " + messages.length + " new messages");
+ }
+ if (messages.length > 0) {
+ fetchMessages(folder, messages);
+ }
+ if (deleteMessages) {
+ deleteMessages(folder, messages);
+ }
+ return messages;
+ }
+
+ /**
+ * Template method that blocks until new messages arrive in the given folder. Typical implementations use {@link
+ * Thread#sleep(long)} or the IMAP IDLE command.
+ *
+ * @param folder the folder to monitor
+ * @throws MessagingException in case of JavaMail errors
+ * @throws InterruptedException when a thread is interrupted
+ */
+ protected abstract void waitForNewMessages(Folder folder) throws MessagingException, InterruptedException;
+
+ /**
+ * Retrieves new messages from the given folder. This implementation creates a {@link SearchTerm} that searches for
+ * all messages in the folder that are {@link Flags.Flag#RECENT RECENT}, not {@link Flags.Flag#ANSWERED ANSWERED},
+ * and not {@link Flags.Flag#DELETED DELETED}. The search term is used to {@link Folder#search(SearchTerm) search}
+ * for new messages.
+ *
+ * @param folder the folder to retrieve new messages from
+ * @return the new messages
+ * @throws MessagingException in case of JavaMail errors
+ */
+ protected Message[] searchForNewMessages(Folder folder) throws MessagingException {
+ if (!folder.isOpen()) {
+ return new Message[0];
+ }
+ Flags supportedFlags = folder.getPermanentFlags();
+ SearchTerm searchTerm = null;
+ if (supportedFlags.contains(Flags.Flag.RECENT)) {
+ searchTerm = new FlagTerm(new Flags(Flags.Flag.RECENT), true);
+ }
+ if (supportedFlags.contains(Flags.Flag.ANSWERED)) {
+ FlagTerm answeredTerm = new FlagTerm(new Flags(Flags.Flag.ANSWERED), false);
+ if (searchTerm == null) {
+ searchTerm = answeredTerm;
+ }
+ else {
+ searchTerm = new AndTerm(searchTerm, answeredTerm);
+ }
+ }
+ if (supportedFlags.contains(Flags.Flag.DELETED)) {
+ FlagTerm deletedTerm = new FlagTerm(new Flags(Flags.Flag.DELETED), false);
+ if (searchTerm == null) {
+ searchTerm = deletedTerm;
+ }
+ else {
+ searchTerm = new AndTerm(searchTerm, deletedTerm);
+ }
+ }
+ return searchTerm != null ? folder.search(searchTerm) : folder.getMessages();
+ }
+
+ /**
+ * Fetches the specified messages from the specified folder. Default implementation {@link Folder#fetch(Message[],
+ * FetchProfile) fetches} every {@link FetchProfile.Item}.
+ *
+ * @param folder the folder to fetch messages from
+ * @param messages the messages to fetch
+ * @throws MessagingException in case of JavMail errors
+ */
+ protected void fetchMessages(Folder folder, Message[] messages) throws MessagingException {
+ FetchProfile contentsProfile = new FetchProfile();
+ contentsProfile.add(FetchProfile.Item.ENVELOPE);
+ contentsProfile.add(FetchProfile.Item.CONTENT_INFO);
+ contentsProfile.add(FetchProfile.Item.FLAGS);
+ folder.fetch(messages, contentsProfile);
+ }
+
+ /**
+ * Deletes the given messages from the given folder. Only invoked when {@link #setDeleteMessages(boolean)} is
+ * true.
+ *
+ * @param folder the folder to delete messages from
+ * @param messages the messages to delete
+ * @throws MessagingException in case of JavaMail errors
+ */
+ protected void deleteMessages(Folder folder, Message[] messages) throws MessagingException {
+ for (int i = 0; i < messages.length; i++) {
+ messages[i].setFlag(Flags.Flag.DELETED, true);
+ }
+ }
+}
diff --git a/support/src/main/java/org/springframework/ws/transport/mail/monitor/ImapIdleMonitoringStrategy.java b/support/src/main/java/org/springframework/ws/transport/mail/monitor/ImapIdleMonitoringStrategy.java
new file mode 100644
index 00000000..b56c3d41
--- /dev/null
+++ b/support/src/main/java/org/springframework/ws/transport/mail/monitor/ImapIdleMonitoringStrategy.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2007 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.mail.monitor;
+
+import javax.mail.Folder;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.event.MessageCountAdapter;
+import javax.mail.event.MessageCountEvent;
+import javax.mail.event.MessageCountListener;
+
+import org.springframework.util.Assert;
+
+import com.sun.mail.imap.IMAPFolder;
+
+/**
+ * Implementation of the {@link MonitoringStrategy} interface that uses the IMAP IDLE command for asynchronous message
+ * detection.
+ *
+ * Note that this implementation is only suitable for use with IMAP servers which support the IDLE command.
+ * Additionally, this strategy requires JavaMail version 1.4.1.
+ *
+ * @author Arjen Poutsma
+ * @since 1.1.0
+ */
+public class ImapIdleMonitoringStrategy extends AbstractMonitoringStrategy {
+
+ private MessageCountListener messageCountListener;
+
+ protected void waitForNewMessages(Folder folder) throws MessagingException, InterruptedException {
+ Assert.isInstanceOf(IMAPFolder.class, folder);
+ IMAPFolder imapFolder = (IMAPFolder) folder;
+ // retrieve unseen messages before we enter the blocking idle call
+ if (searchForNewMessages(folder).length > 0) {
+ return;
+ }
+ if (messageCountListener == null) {
+ createMessageCountListener();
+ }
+ folder.addMessageCountListener(messageCountListener);
+ try {
+ imapFolder.idle();
+ }
+ finally {
+ folder.removeMessageCountListener(messageCountListener);
+ }
+ }
+
+ private void createMessageCountListener() {
+ messageCountListener = new MessageCountAdapter() {
+ public void messagesAdded(MessageCountEvent e) {
+ Message[] messages = e.getMessages();
+ for (int i = 0; i < messages.length; i++) {
+ try {
+ // this will return the flow to the idle call, above
+ messages[i].getLineCount();
+ }
+ catch (MessagingException ex) {
+ // ignore
+ }
+ }
+ }
+ };
+ }
+}
diff --git a/support/src/main/java/org/springframework/ws/transport/mail/monitor/MonitoringStrategy.java b/support/src/main/java/org/springframework/ws/transport/mail/monitor/MonitoringStrategy.java
new file mode 100644
index 00000000..0de64e54
--- /dev/null
+++ b/support/src/main/java/org/springframework/ws/transport/mail/monitor/MonitoringStrategy.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2007 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.mail.monitor;
+
+import javax.mail.Folder;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+
+/**
+ * Defines the contract for objects that monitor a given folder for new messages. Allows for multiple implementation
+ * strategies, including polling, or event-driven techniques such as IMAP's IDLE command.
+ *
+ * @author Arjen Poutsma
+ */
+public interface MonitoringStrategy {
+
+ /**
+ * Monitors the given folder, and returns any new messages when they arrive.
+ *
+ * @param folder the folder in which to look for new messages
+ * @return the new messages
+ * @throws MessagingException in case of JavaMail errors
+ * @throws InterruptedException if a thread is interrupted
+ */
+ Message[] monitor(Folder folder) throws MessagingException, InterruptedException;
+
+ /**
+ * Returns the folder open mode to be used by this strategy. Can be either {@link Folder#READ_ONLY} or {@link
+ * Folder#READ_WRITE}.
+ */
+ int getFolderOpenMode();
+
+}
diff --git a/support/src/main/java/org/springframework/ws/transport/mail/monitor/PollingMonitoringStrategy.java b/support/src/main/java/org/springframework/ws/transport/mail/monitor/PollingMonitoringStrategy.java
new file mode 100644
index 00000000..130300ae
--- /dev/null
+++ b/support/src/main/java/org/springframework/ws/transport/mail/monitor/PollingMonitoringStrategy.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2007 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.mail.monitor;
+
+import javax.mail.Folder;
+import javax.mail.MessagingException;
+
+/**
+ * Implementation of the {@link MonitoringStrategy} interface that uses a simple polling mechanism. Defines a {@link
+ * #setPollingInterval(long) polling interval} property which defines the interval in between message polls.
+ *
+ * Note that this implementation is not suitable for use with POP3 servers. Use the {@link
+ * Pop3PollingMonitoringStrategy} instead.
+ *
+ * @author Arjen Poutsma
+ * @since 1.1.0
+ */
+public class PollingMonitoringStrategy extends AbstractMonitoringStrategy {
+
+ /** Defines the default polling frequency. Set to 1000 * 60 milliseconds (i.e. 1 minute). */
+ public static final long DEFAULT_POLLING_FREQUENCY = 1000 * 60;
+
+ private long pollingInterval = DEFAULT_POLLING_FREQUENCY;
+
+ /**
+ * Sets the interval used in between message polls, in milliseconds. The default is 1000 * 60 ms,
+ * that is 1 minute.
+ */
+ public void setPollingInterval(long pollingInterval) {
+ this.pollingInterval = pollingInterval;
+ }
+
+ protected void waitForNewMessages(Folder folder) throws MessagingException, InterruptedException {
+ Thread.sleep(pollingInterval);
+ afterSleep(folder);
+ }
+
+ /**
+ * Invoked after the {@link Thread#sleep(long)} method has been invoked. This implementation calls {@link
+ * Folder#getMessageCount(), to force new messages to be seen.
+ *
+ * @param folder the folder to check for new messages
+ * @throws MessagingException in case of JavaMail errors
+ */
+ protected void afterSleep(Folder folder) throws MessagingException {
+ folder.getMessageCount();
+ }
+
+}
diff --git a/support/src/main/java/org/springframework/ws/transport/mail/monitor/Pop3PollingMonitoringStrategy.java b/support/src/main/java/org/springframework/ws/transport/mail/monitor/Pop3PollingMonitoringStrategy.java
new file mode 100644
index 00000000..1272e667
--- /dev/null
+++ b/support/src/main/java/org/springframework/ws/transport/mail/monitor/Pop3PollingMonitoringStrategy.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2007 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.mail.monitor;
+
+import javax.mail.Folder;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+
+import org.springframework.ws.transport.mail.support.MailTransportUtils;
+
+/**
+ * Implementation of the {@link MonitoringStrategy} interface that uses a simple polling mechanism suitable for POP3
+ * servers. Since POP3 does not have a native mechanism to determine which messages are "new", this implementation
+ * simply retrieves all messages in the {@link Folder}, and delete them afterwards. All messages in the POP3 mailbox are
+ * therefore, by definition, new.
+ *
+ * Setting the {@link #setDeleteMessages(boolean) deleteMessages} property is therefore ignored: messages are always
+ * deleted.
+ *
+ * @author Arjen Poutsma
+ * @since 1.1.0
+ */
+public class Pop3PollingMonitoringStrategy extends PollingMonitoringStrategy {
+
+ public Pop3PollingMonitoringStrategy() {
+ super.setDeleteMessages(true);
+ }
+
+ public void setDeleteMessages(boolean deleteMessages) {
+ }
+
+ /** Re-opens the folder, if it closed. */
+ protected void afterSleep(Folder folder) throws MessagingException {
+ if (!folder.isOpen()) {
+ folder.open(Folder.READ_WRITE);
+ }
+ }
+
+ /** Simply returns {@link Folder#getMessages()}. */
+ protected Message[] searchForNewMessages(Folder folder) throws MessagingException {
+ return folder.getMessages();
+ }
+
+ /**
+ * Deletes the given messages from the given folder, and closes it to expunge deleted messages.
+ *
+ * @param folder the folder to delete messages from
+ * @param messages the messages to delete
+ * @throws MessagingException in case of JavaMail errors
+ */
+ protected void deleteMessages(Folder folder, Message[] messages) throws MessagingException {
+ super.deleteMessages(folder, messages);
+ // expunge deleted mails, and make sure we've retrieved them before closing the folder
+ for (int i = 0; i < messages.length; i++) {
+ new MimeMessage((MimeMessage) messages[i]);
+ }
+ MailTransportUtils.closeFolder(folder, true);
+ }
+}
\ No newline at end of file
diff --git a/support/src/main/java/org/springframework/ws/transport/mail/monitor/package.html b/support/src/main/java/org/springframework/ws/transport/mail/monitor/package.html
new file mode 100644
index 00000000..43f8e7c7
--- /dev/null
+++ b/support/src/main/java/org/springframework/ws/transport/mail/monitor/package.html
@@ -0,0 +1,6 @@
+
+
+Provides the MonitoringStrategy interface and implementations. Used for monitoring a JavaMail Folder for new email
+messages.
+
+
\ No newline at end of file
diff --git a/support/src/main/java/org/springframework/ws/transport/mail/package.html b/support/src/main/java/org/springframework/ws/transport/mail/package.html
new file mode 100644
index 00000000..18e796fc
--- /dev/null
+++ b/support/src/main/java/org/springframework/ws/transport/mail/package.html
@@ -0,0 +1,5 @@
+
+
+Package providing support for handling messages via email.
+
+
diff --git a/support/src/main/java/org/springframework/ws/transport/mail/support/MailTransportUtils.java b/support/src/main/java/org/springframework/ws/transport/mail/support/MailTransportUtils.java
new file mode 100644
index 00000000..b356ac2e
--- /dev/null
+++ b/support/src/main/java/org/springframework/ws/transport/mail/support/MailTransportUtils.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2007 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.mail.support;
+
+import java.net.URI;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.mail.Folder;
+import javax.mail.MessagingException;
+import javax.mail.Service;
+import javax.mail.Store;
+import javax.mail.Transport;
+import javax.mail.URLName;
+import javax.mail.internet.AddressException;
+import javax.mail.internet.InternetAddress;
+
+import org.springframework.util.StringUtils;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Collection of utility methods to work with Mail transports.
+ *
+ * @author Arjen Poutsma
+ * @since 1.1.0
+ */
+public abstract class MailTransportUtils {
+
+ private static final Pattern TO_PATTERN = Pattern.compile("^([^\\?]+)");
+
+ private static final Pattern SUBJECT_PATTERN = Pattern.compile("subject=([^\\&]+)");
+
+ private static final Log logger = LogFactory.getLog(MailTransportUtils.class);
+
+ private MailTransportUtils() {
+ }
+
+ public static InternetAddress getTo(URI uri) {
+ Matcher matcher = TO_PATTERN.matcher(uri.getSchemeSpecificPart());
+ if (matcher.find()) {
+ for (int i = 1; i <= matcher.groupCount(); i++) {
+ String group = matcher.group(i);
+ if (group != null) {
+ try {
+ return new InternetAddress(group);
+ }
+ catch (AddressException e) {
+ // try next group
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ public static String getSubject(URI uri) {
+ Matcher matcher = SUBJECT_PATTERN.matcher(uri.getSchemeSpecificPart());
+ if (matcher.find()) {
+ return matcher.group(1);
+ }
+ return null;
+ }
+
+ /**
+ * Close the given JavaMail Service and ignore any thrown exception. This is useful for typical finally
+ * blocks in manual JavaMail code.
+ *
+ * @param service the JavaMail Service to close (may be null)
+ * @see Transport
+ * @see Store
+ */
+ public static void closeService(Service service) {
+ if (service != null) {
+ try {
+ service.close();
+ }
+ catch (MessagingException ex) {
+ logger.debug("Could not close JavaMail Service", ex);
+ }
+ }
+ }
+
+ /**
+ * Close the given JavaMail Folder and ignore any thrown exception. This is useful for typical finally
+ * blocks in manual JavaMail code.
+ *
+ * @param folder the JavaMail Folder to close (may be null)
+ */
+
+ public static void closeFolder(Folder folder) {
+ closeFolder(folder, false);
+ }
+
+ /**
+ * Close the given JavaMail Folder and ignore any thrown exception. This is useful for typical finally
+ * blocks in manual JavaMail code.
+ *
+ * @param folder the JavaMail Folder to close (may be null)
+ * @param expunge whether all deleted messages should be expunged from the folder
+ */
+ public static void closeFolder(Folder folder, boolean expunge) {
+ if (folder != null && folder.isOpen()) {
+ try {
+ folder.close(expunge);
+ }
+ catch (MessagingException ex) {
+ logger.debug("Could not close JavaMail Folder", ex);
+ }
+ }
+ }
+
+ /** Returns a string representation of the given {@link URLName}, where the password has been protected. */
+ public static String toPasswordProtectedString(URLName name) {
+ String protocol = name.getProtocol();
+ String username = name.getUsername();
+ String password = name.getPassword();
+ String host = name.getHost();
+ int port = name.getPort();
+ String file = name.getFile();
+ String ref = name.getRef();
+ StringBuffer tempURL = new StringBuffer();
+ if (protocol != null) {
+ tempURL.append(protocol).append(':');
+ }
+
+ if (StringUtils.hasLength(username) || StringUtils.hasLength(null)) {
+ tempURL.append("//");
+ if (StringUtils.hasLength(username)) {
+ tempURL.append(username);
+ if (StringUtils.hasLength(password)) {
+ tempURL.append(":*****");
+ }
+ tempURL.append("@");
+ }
+ if (StringUtils.hasLength(host)) {
+ tempURL.append(host);
+ }
+ if (port != -1) {
+ tempURL.append(':').append(Integer.toString(port));
+ }
+ if (StringUtils.hasLength(file)) {
+ tempURL.append('/');
+ }
+ }
+ if (StringUtils.hasLength(file)) {
+ tempURL.append(file);
+ }
+ if (StringUtils.hasLength(ref)) {
+ tempURL.append('#').append(ref);
+ }
+ return tempURL.toString();
+ }
+
+
+}
diff --git a/support/src/main/java/org/springframework/ws/transport/mail/support/package.html b/support/src/main/java/org/springframework/ws/transport/mail/support/package.html
new file mode 100644
index 00000000..d409f6cb
--- /dev/null
+++ b/support/src/main/java/org/springframework/ws/transport/mail/support/package.html
@@ -0,0 +1,5 @@
+
+
+Classes supporting the org.springframework.ws.transport.mail package.
+
+
\ No newline at end of file
diff --git a/sandbox/src/main/java/org/springframework/ws/transport/support/AbstractMultiThreadedMessageReceiver.java b/support/src/main/java/org/springframework/ws/transport/support/AbstractAsyncStandaloneMessageReceiver.java
similarity index 84%
rename from sandbox/src/main/java/org/springframework/ws/transport/support/AbstractMultiThreadedMessageReceiver.java
rename to support/src/main/java/org/springframework/ws/transport/support/AbstractAsyncStandaloneMessageReceiver.java
index 517fe756..c42076cc 100644
--- a/sandbox/src/main/java/org/springframework/ws/transport/support/AbstractMultiThreadedMessageReceiver.java
+++ b/support/src/main/java/org/springframework/ws/transport/support/AbstractAsyncStandaloneMessageReceiver.java
@@ -19,16 +19,16 @@ package org.springframework.ws.transport.support;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;
-import org.springframework.util.ClassUtils;
import org.springframework.scheduling.commonj.WorkManagerTaskExecutor;
+import org.springframework.util.ClassUtils;
/**
- * Abstract base class for standalone, server-side transport objects. Contains a Spring {@link TaskExecutor}, and
- * various lifecycle callbacks.
+ * Abstract base class for asynchronous standalone, server-side transport objects. Contains a Spring {@link
+ * TaskExecutor}, and various lifecycle callbacks.
*
* @author Arjen Poutsma
*/
-public abstract class AbstractMultiThreadedMessageReceiver extends AbstractStandaloneMessagingReceiver
+public abstract class AbstractAsyncStandaloneMessageReceiver extends AbstractStandaloneMessageReceiver
implements BeanNameAware {
/** Default thread name prefix. */
@@ -38,11 +38,6 @@ public abstract class AbstractMultiThreadedMessageReceiver extends AbstractStand
private String beanName;
- /** Returns the task executor. */
- public TaskExecutor getTaskExecutor() {
- return taskExecutor;
- }
-
/**
* Set the Spring {@link TaskExecutor} to use for running the listener threads. Default is {@link
* SimpleAsyncTaskExecutor}, starting up a number of new threads.
@@ -77,4 +72,13 @@ public abstract class AbstractMultiThreadedMessageReceiver extends AbstractStand
String threadNamePrefix = beanName != null ? beanName + "-" : DEFAULT_THREAD_NAME_PREFIX;
return new SimpleAsyncTaskExecutor(threadNamePrefix);
}
+
+ /**
+ * Executes the given {@link Runnable} via this receiver's {@link TaskExecutor}.
+ *
+ * @see #setTaskExecutor(TaskExecutor)
+ */
+ protected void execute(Runnable runnable) {
+ taskExecutor.execute(runnable);
+ }
}
diff --git a/sandbox/src/main/java/org/springframework/ws/transport/support/AbstractStandaloneMessagingReceiver.java b/support/src/main/java/org/springframework/ws/transport/support/AbstractStandaloneMessageReceiver.java
similarity index 73%
rename from sandbox/src/main/java/org/springframework/ws/transport/support/AbstractStandaloneMessagingReceiver.java
rename to support/src/main/java/org/springframework/ws/transport/support/AbstractStandaloneMessageReceiver.java
index fb3676f9..701e0b16 100644
--- a/sandbox/src/main/java/org/springframework/ws/transport/support/AbstractStandaloneMessagingReceiver.java
+++ b/support/src/main/java/org/springframework/ws/transport/support/AbstractStandaloneMessageReceiver.java
@@ -19,8 +19,14 @@ package org.springframework.ws.transport.support;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.Lifecycle;
-/** @author Arjen Poutsma */
-public abstract class AbstractStandaloneMessagingReceiver extends SimpleWebServiceMessageReceiverObjectSupport
+/**
+ * Abstract base class for standalone, server-side transport objects. Provides a basic, thread-safe implementation of
+ * the {@link Lifecycle} interface, and various template methods to be implemented by concrete sub classes.
+ *
+ * @author Arjen Poutsma
+ * @since 1.1.0
+ */
+public abstract class AbstractStandaloneMessageReceiver extends SimpleWebServiceMessageReceiverObjectSupport
implements Lifecycle, DisposableBean {
private volatile boolean active = false;
@@ -54,20 +60,20 @@ public abstract class AbstractStandaloneMessagingReceiver extends SimpleWebServi
this.autoStartup = autoStartup;
}
+ /** Calls {@link #activate()} when the BeanFactory initializes the receiver instance. */
public void afterPropertiesSet() throws Exception {
activate();
}
- /**
- * Calls shutdown when the BeanFactory destroys the server instance.
- *
- * @see #shutdown()
- */
+ /** Calls {@link #shutdown()} when the BeanFactory destroys the receiver instance. */
public void destroy() {
shutdown();
}
- /** Initialize this server. Starts the server if autoStartup hasn't been turned off. */
+ /**
+ * Initialize this server. Starts the server if {@link #setAutoStartup(boolean) autoStartup} hasn't been turned
+ * off.
+ */
public final void activate() throws Exception {
synchronized (lifecycleMonitor) {
active = true;
@@ -97,7 +103,7 @@ public abstract class AbstractStandaloneMessagingReceiver extends SimpleWebServi
onStop();
}
- /** Shut down the registered listeners and close this listener container. */
+ /** Shut down this server. */
public final void shutdown() {
synchronized (lifecycleMonitor) {
running = false;
@@ -107,11 +113,19 @@ public abstract class AbstractStandaloneMessagingReceiver extends SimpleWebServi
onShutdown();
}
+ /**
+ * Template method invoked when {@link #activate()} is invoked.
+ *
+ * @throws Exception in case of errors
+ */
protected abstract void onActivate() throws Exception;
+ /** Template method invoked when {@link #start()} is invoked. */
protected abstract void onStart();
+ /** Template method invoked when {@link #stop()} is invoked. */
protected abstract void onStop();
+ /** Template method invoked when {@link #shutdown()} is invoked. */
protected abstract void onShutdown();
}
diff --git a/support/src/test/java/org/springframework/ws/transport/mail/support/MailTransportUtilsTest.java b/support/src/test/java/org/springframework/ws/transport/mail/support/MailTransportUtilsTest.java
new file mode 100644
index 00000000..2dbef069
--- /dev/null
+++ b/support/src/test/java/org/springframework/ws/transport/mail/support/MailTransportUtilsTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright ${YEAR} 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.mail.support;
+
+import java.net.URI;
+import javax.mail.URLName;
+import javax.mail.internet.InternetAddress;
+
+import junit.framework.TestCase;
+
+public class MailTransportUtilsTest extends TestCase {
+
+ public void testToPasswordProtectedString() throws Exception {
+ URLName name = new URLName("imap://john:secret@imap.example.com/INBOX");
+ String result = MailTransportUtils.toPasswordProtectedString(name);
+ assertEquals("Password found in string", -1, result.indexOf("secret"));
+ }
+
+ public void testGetTo() throws Exception {
+ URI uri = new URI("mailto:infobot@example.com?subject=current-issue");
+ InternetAddress to = MailTransportUtils.getTo(uri);
+ assertEquals("Invalid destination", new InternetAddress("infobot@example.com"), to);
+
+ uri = new URI("mailto:infobot@example.com");
+ to = MailTransportUtils.getTo(uri);
+ assertEquals("Invalid destination", new InternetAddress("infobot@example.com"), to);
+ }
+
+ public void testGetSubject() throws Exception {
+ URI uri = new URI("mailto:infobot@example.com?subject=current-issue");
+ String subject = MailTransportUtils.getSubject(uri);
+ assertEquals("Invalid destination", "current-issue", subject);
+
+ uri = new URI("mailto:infobot@example.com");
+ subject = MailTransportUtils.getSubject(uri);
+ assertNull("Invalid destination", subject);
+ }
+}
\ No newline at end of file