SWS-139: mail support
This commit is contained in:
@@ -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 <code>true</code>.
|
||||
*/
|
||||
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}.
|
||||
* <p/>
|
||||
* Return <code>null</code> 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 <code>WebServiceMessageReceiver</code> used by this listener.
|
||||
*/
|
||||
public WebServiceMessageReceiver getMessageReceiver() {
|
||||
return messageReceiver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the <code>WebServiceMessageReceiver</code> 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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <ajwp@xs4all.nl>");
|
||||
messageFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL);
|
||||
}
|
||||
|
||||
public void testSendAndReceiveQueueNoResponse() throws Exception {
|
||||
WebServiceConnection connection = null;
|
||||
try {
|
||||
connection = messageSender.createConnection(URI);
|
||||
SOAPMessage saajMessage = messageFactory.createMessage();
|
||||
saajMessage.getSOAPBody().addBodyElement(new QName("http://springframework.org", "test"));
|
||||
SoapMessage soapRequest = new SaajSoapMessage(saajMessage);
|
||||
soapRequest.setSoapAction(SOAP_ACTION);
|
||||
connection.send(soapRequest);
|
||||
// SoapMessage response = (SoapMessage) connection.receive(new SaajSoapMessageFactory(messageFactory));
|
||||
}
|
||||
finally {
|
||||
if (connection != null) {
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
|
||||
|
||||
<bean id="messagingReceiver" class="org.springframework.ws.transport.mail.MailMessageReceiver">
|
||||
<property name="messageFactory" ref="messageFactory"/>
|
||||
<property name="messageReceiver">
|
||||
<bean class="org.springframework.ws.transport.SimpleTestingMessageReceiver"/>
|
||||
</property>
|
||||
<property name="monitoringStrategy">
|
||||
<bean class="org.springframework.ws.transport.mail.DefaultMonitoringStrategy">
|
||||
<property name="pollingInterval" value="10000"/>
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
|
||||
|
||||
<bean id="mbeanExporter" class="org.springframework.jmx.export.MBeanExporter">
|
||||
<property name="beans">
|
||||
<map>
|
||||
<entry key="spring-ws:service=messagingContainer">
|
||||
<ref local="messagingReceiver"/>
|
||||
</entry>
|
||||
</map>
|
||||
</property>
|
||||
<property name="assembler">
|
||||
<bean class="org.springframework.jmx.export.assembler.InterfaceBasedMBeanInfoAssembler">
|
||||
<property name="interfaceMappings">
|
||||
<props>
|
||||
<prop key="spring-ws:service=messagingContainer">org.springframework.context.Lifecycle</prop>
|
||||
</props>
|
||||
</property>
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
|
||||
</beans>
|
||||
@@ -98,12 +98,16 @@
|
||||
<dependency>
|
||||
<groupId>javax.jms</groupId>
|
||||
<artifactId>jms</artifactId>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.ejb</groupId>
|
||||
<artifactId>ejb</artifactId>
|
||||
<version>2.1</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.mail</groupId>
|
||||
<artifactId>mail</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!-- Other dependencies -->
|
||||
|
||||
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* 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.util.Properties;
|
||||
import javax.mail.Folder;
|
||||
import javax.mail.FolderClosedException;
|
||||
import javax.mail.Message;
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.Session;
|
||||
import javax.mail.Store;
|
||||
import javax.mail.URLName;
|
||||
import javax.mail.internet.AddressException;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
|
||||
import org.springframework.scheduling.SchedulingAwareRunnable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.ws.WebServiceMessageFactory;
|
||||
import org.springframework.ws.transport.WebServiceMessageReceiver;
|
||||
import org.springframework.ws.transport.mail.monitor.MonitoringStrategy;
|
||||
import org.springframework.ws.transport.mail.monitor.PollingMonitoringStrategy;
|
||||
import org.springframework.ws.transport.mail.monitor.Pop3PollingMonitoringStrategy;
|
||||
import org.springframework.ws.transport.mail.support.MailTransportUtils;
|
||||
import org.springframework.ws.transport.support.AbstractAsyncStandaloneMessageReceiver;
|
||||
|
||||
/**
|
||||
* Server-side component for receiving email messages using JavaMail. Requires a {@link #setTransportUri(String)
|
||||
* transport} URI, {@link #setStoreUri(String) store} URI, and {@link #setMonitoringStrategy(MonitoringStrategy)
|
||||
* monitoringStrategy} to be set, in addition to the {@link #setMessageFactory(WebServiceMessageFactory) messageFactory}
|
||||
* and {@link #setMessageReceiver(WebServiceMessageReceiver) messageReceiver} required by the base class.
|
||||
* <p/>
|
||||
* The {@link MonitoringStrategy} is used to detect new incoming email request. If the <code>monitoringStrategy</code>
|
||||
* 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}.
|
||||
* <p/>
|
||||
* A new {@link Session} will be created with those properties. Use either this method or {@link #setSession}, but
|
||||
* not both.
|
||||
* <p/>
|
||||
* Non-default properties in this instance will override given JavaMail properties.
|
||||
*/
|
||||
public void setJavaMailProperties(Properties javaMailProperties) {
|
||||
session = Session.getInstance(javaMailProperties, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JavaMail <code>Session</code>, possibly pulled from JNDI.
|
||||
* <p/>
|
||||
* Default is a new <code>Session</code> without defaults, that is completely configured via this instance's
|
||||
* properties.
|
||||
* <p/>
|
||||
* If using a pre-configured <code>Session</code>, non-default properties in this instance will override the
|
||||
* settings in the <code>Session</code>.
|
||||
*
|
||||
* @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
|
||||
* <code>[imap|pop3]://user:password@host:port/INBOX</code>. Setting this property is required.
|
||||
* <p/>
|
||||
* For example, <code>imap://john:secret@imap.example.com/INBOX</code>
|
||||
*
|
||||
* @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
|
||||
* <code>smtp://user:password@host:port</code>. Setting this property is required.
|
||||
* <p/>
|
||||
* For example, <code>smtp://john:secret@smtp.example.com</code>
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
* <p/>
|
||||
* 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.
|
||||
* <p/>
|
||||
* This message sender supports URI's of the following format: <blockquote> <tt><b>mailto:</b></tt><i>to</i>[<tt><b>?</b></tt><i>param-name</i><tt><b>=</b></tt><i>param-value</i>][<tt><b>&</b></tt><i>param-name</i><tt><b>=</b></tt><i>param-value</i>]*
|
||||
* </blockquote> where the characters <tt><b>:</b></tt>, <tt><b>?</b></tt>, and <tt><b>&</b></tt> stand for
|
||||
* themselves. The <i>to</i> represents a RFC 822 mailbox. Valid <i>param-name</i> include:
|
||||
* <p/>
|
||||
* <blockquote><table> <tr><th><i>param-name</i></th><th><i>Description</i></th></tr>
|
||||
* <tr><td><tt>subject</tt></td><td>The subject of the request message.</td></tr> </table></blockquote>
|
||||
* <p/>
|
||||
* Some examples of email URIs are:
|
||||
* <p/>
|
||||
* <blockquote><tt>mailto:john@example.com</tt><br> <tt>mailto:john@example.com@?subject=SOAP%20Test</tt><br></blockquote>
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @see <a href="http://www.ietf.org/rfc/rfc2368.txt">The mailto URL scheme</a>
|
||||
* @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}.
|
||||
* <p/>
|
||||
* A new {@link Session} will be created with those properties. Use either this method or {@link #setSession}, but
|
||||
* not both.
|
||||
* <p/>
|
||||
* 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, <strong>in milliseconds</strong>. The default is 1000 * 60 ms, that
|
||||
* is 1 minute.
|
||||
*/
|
||||
public void setReceiveSleepTime(long receiveSleepTime) {
|
||||
this.receiveSleepTime = receiveSleepTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JavaMail <code>Session</code>, possibly pulled from JNDI.
|
||||
* <p/>
|
||||
* Default is a new <code>Session</code> without defaults, that is completely configured via this instance's
|
||||
* properties.
|
||||
* <p/>
|
||||
* If using a pre-configured <code>Session</code>, non-default properties in this instance will override the
|
||||
* settings in the <code>Session</code>.
|
||||
*
|
||||
* @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
|
||||
* <code>[imap|pop3]://user:password@host:port/INBOX</code>. Setting this property is required.
|
||||
* <p/>
|
||||
* For example, <code>imap://john:secret@imap.example.com/INBOX</code>
|
||||
*
|
||||
* @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
|
||||
* <code>smtp://user:password@host:port</code>. Setting this property is required.
|
||||
* <p/>
|
||||
* For example, <code>smtp://john:secret@smtp.example.com</code>
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 <code>true</code>.
|
||||
*/
|
||||
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 <code>true</code>.
|
||||
*
|
||||
* @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
|
||||
* <code>true</code>.
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
* <p/>
|
||||
* <b>Note</b> 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
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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 <code>IDLE</code> 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();
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
* <p/>
|
||||
* <b>Note</b> 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, <strong>in milliseconds</strong>. 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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
* <p/>
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<body>
|
||||
Provides the MonitoringStrategy interface and implementations. Used for monitoring a JavaMail Folder for new email
|
||||
messages.
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
Package providing support for handling messages via email.
|
||||
</body>
|
||||
</html>
|
||||
@@ -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 <code>finally</code>
|
||||
* blocks in manual JavaMail code.
|
||||
*
|
||||
* @param service the JavaMail Service to close (may be <code>null</code>)
|
||||
* @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 <code>finally</code>
|
||||
* blocks in manual JavaMail code.
|
||||
*
|
||||
* @param folder the JavaMail Folder to close (may be <code>null</code>)
|
||||
*/
|
||||
|
||||
public static void closeFolder(Folder folder) {
|
||||
closeFolder(folder, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the given JavaMail Folder and ignore any thrown exception. This is useful for typical <code>finally</code>
|
||||
* blocks in manual JavaMail code.
|
||||
*
|
||||
* @param folder the JavaMail Folder to close (may be <code>null</code>)
|
||||
* @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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
Classes supporting the org.springframework.ws.transport.mail package.
|
||||
</body>
|
||||
</html>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 <code>shutdown</code> 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 <code>autoStartup</code> 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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user