/* * Copyright 2005-2014 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 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} * 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.5.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 response 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}, possibly pulled from JNDI. * * <p>Default is a new {@code Session} without defaults, that is completely configured via this instance's * properties. * * <p>If using a pre-configured {@code Session}, non-default properties in this instance will override the * settings in the {@code 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 * {@code [imap|pop3]://user:password@host:port/INBOX}. Setting this property is required. * * <p>For example, {@code 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 * {@code smtp://user:password@host:port}. Setting this property is required. * * <p>For example, {@code 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; } @Override 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(); } @Override protected void onActivate() throws MessagingException { openSession(); openFolder(); } @Override protected void onStart() { if (logger.isInfoEnabled()) { logger.info("Starting mail receiver [" + MailTransportUtils.toPasswordProtectedString(storeUri) + "]"); } execute(new MonitoringRunnable()); } @Override protected void onStop() { if (logger.isInfoEnabled()) { logger.info("Stopping mail receiver [" + MailTransportUtils.toPasswordProtectedString(storeUri) + "]"); } closeFolder(); } @Override 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 { @Override public void run() { try { openFolder(); while (isRunning()) { try { Message[] messages = monitoringStrategy.monitor(folder); for (Message message : messages) { MessageHandler handler = new MessageHandler(message); 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); } } @Override public boolean isLongLived() { return true; } } private class MessageHandler implements SchedulingAwareRunnable { private final Message message; public MessageHandler(Message message) { this.message = message; } @Override 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); } } @Override public boolean isLongLived() { return false; } } }