View Javadoc

1   /*
2    * Copyright 2005-2010 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.ws.transport.mail;
18  
19  import java.util.Properties;
20  import javax.mail.Folder;
21  import javax.mail.FolderClosedException;
22  import javax.mail.Message;
23  import javax.mail.MessagingException;
24  import javax.mail.Session;
25  import javax.mail.Store;
26  import javax.mail.URLName;
27  import javax.mail.internet.AddressException;
28  import javax.mail.internet.InternetAddress;
29  
30  import org.springframework.scheduling.SchedulingAwareRunnable;
31  import org.springframework.util.Assert;
32  import org.springframework.ws.WebServiceMessageFactory;
33  import org.springframework.ws.transport.WebServiceMessageReceiver;
34  import org.springframework.ws.transport.mail.monitor.MonitoringStrategy;
35  import org.springframework.ws.transport.mail.monitor.PollingMonitoringStrategy;
36  import org.springframework.ws.transport.mail.monitor.Pop3PollingMonitoringStrategy;
37  import org.springframework.ws.transport.mail.support.MailTransportUtils;
38  import org.springframework.ws.transport.support.AbstractAsyncStandaloneMessageReceiver;
39  
40  /**
41   * Server-side component for receiving email messages using JavaMail.  Requires a {@link #setTransportUri(String)
42   * transport} URI, {@link #setStoreUri(String) store} URI, and {@link #setMonitoringStrategy(MonitoringStrategy)
43   * monitoringStrategy} to be set, in addition to the {@link #setMessageFactory(WebServiceMessageFactory) messageFactory}
44   * and {@link #setMessageReceiver(WebServiceMessageReceiver) messageReceiver} required by the base class.
45   * <p/>
46   * The {@link MonitoringStrategy} is used to detect new incoming email request. If the <code>monitoringStrategy</code>
47   * is not explicitly set, this receiver will use the {@link Pop3PollingMonitoringStrategy} for POP3 servers, and the
48   * {@link PollingMonitoringStrategy} for IMAP servers.
49   *
50   * @author Arjen Poutsma
51   * @since 1.5.0
52   */
53  public class MailMessageReceiver extends AbstractAsyncStandaloneMessageReceiver {
54  
55      private Session session = Session.getInstance(new Properties(), null);
56  
57      private URLName storeUri;
58  
59      private URLName transportUri;
60  
61      private Folder folder;
62  
63      private Store store;
64  
65      private InternetAddress from;
66  
67      private MonitoringStrategy monitoringStrategy;
68  
69      /** Sets the from address to use when sending response messages. */
70      public void setFrom(String from) throws AddressException {
71          this.from = new InternetAddress(from);
72      }
73  
74      /**
75       * Set JavaMail properties for the {@link Session}.
76       * <p/>
77       * A new {@link Session} will be created with those properties. Use either this method or {@link #setSession}, but
78       * not both.
79       * <p/>
80       * Non-default properties in this instance will override given JavaMail properties.
81       */
82      public void setJavaMailProperties(Properties javaMailProperties) {
83          session = Session.getInstance(javaMailProperties, null);
84      }
85  
86      /**
87       * Set the JavaMail <code>Session</code>, possibly pulled from JNDI.
88       * <p/>
89       * Default is a new <code>Session</code> without defaults, that is completely configured via this instance's
90       * properties.
91       * <p/>
92       * If using a pre-configured <code>Session</code>, non-default properties in this instance will override the
93       * settings in the <code>Session</code>.
94       *
95       * @see #setJavaMailProperties
96       */
97      public void setSession(Session session) {
98          Assert.notNull(session, "Session must not be null");
99          this.session = session;
100     }
101 
102     /**
103      * Sets the JavaMail Store URI to be used for retrieving request messages. Typically takes the form of
104      * <code>[imap|pop3]://user:password@host:port/INBOX</code>. Setting this property is required.
105      * <p/>
106      * For example, <code>imap://john:secret@imap.example.com/INBOX</code>
107      *
108      * @see Session#getStore(URLName)
109      */
110     public void setStoreUri(String storeUri) {
111         this.storeUri = new URLName(storeUri);
112     }
113 
114     /**
115      * Sets the JavaMail Transport URI to be used for sending response messages. Typically takes the form of
116      * <code>smtp://user:password@host:port</code>. Setting this property is required.
117      * <p/>
118      * For example, <code>smtp://john:secret@smtp.example.com</code>
119      *
120      * @see Session#getTransport(URLName)
121      */
122     public void setTransportUri(String transportUri) {
123         this.transportUri = new URLName(transportUri);
124     }
125 
126     /**
127      * Sets the monitoring strategy to use for retrieving new requests. Default is the {@link
128      * PollingMonitoringStrategy}.
129      */
130     public void setMonitoringStrategy(MonitoringStrategy monitoringStrategy) {
131         this.monitoringStrategy = monitoringStrategy;
132     }
133 
134     @Override
135     public void afterPropertiesSet() throws Exception {
136         Assert.notNull(storeUri, "Property 'storeUri' is required");
137         Assert.notNull(transportUri, "Property 'transportUri' is required");
138         if (monitoringStrategy == null) {
139             String protocol = storeUri.getProtocol();
140             if ("pop3".equals(protocol)) {
141                 monitoringStrategy = new Pop3PollingMonitoringStrategy();
142             }
143             else if ("imap".equals(protocol)) {
144                 monitoringStrategy = new PollingMonitoringStrategy();
145             }
146             else {
147                 throw new IllegalArgumentException("Cannot determine monitoring strategy for \"" + protocol + "\". " +
148                         "Set the 'monitoringStrategy' explicitly.");
149             }
150         }
151         super.afterPropertiesSet();
152     }
153 
154     @Override
155     protected void onActivate() throws MessagingException {
156         openSession();
157         openFolder();
158     }
159 
160     @Override
161     protected void onStart() {
162         if (logger.isInfoEnabled()) {
163             logger.info("Starting mail receiver [" + MailTransportUtils.toPasswordProtectedString(storeUri) + "]");
164         }
165         execute(new MonitoringRunnable());
166     }
167 
168     @Override
169     protected void onStop() {
170         if (logger.isInfoEnabled()) {
171             logger.info("Stopping mail receiver [" + MailTransportUtils.toPasswordProtectedString(storeUri) + "]");
172         }
173         closeFolder();
174     }
175 
176     @Override
177     protected void onShutdown() {
178         if (logger.isInfoEnabled()) {
179             logger.info("Shutting down mail receiver [" + MailTransportUtils.toPasswordProtectedString(storeUri) + "]");
180         }
181         closeFolder();
182         closeSession();
183     }
184 
185     private void openSession() throws MessagingException {
186         store = session.getStore(storeUri);
187         if (logger.isDebugEnabled()) {
188             logger.debug("Connecting to store [" + MailTransportUtils.toPasswordProtectedString(storeUri) + "]");
189         }
190         store.connect();
191     }
192 
193     private void openFolder() throws MessagingException {
194         if (folder != null && folder.isOpen()) {
195             return;
196         }
197         folder = store.getFolder(storeUri);
198         if (folder == null || !folder.exists()) {
199             throw new IllegalStateException("No default folder to receive from");
200         }
201         if (logger.isDebugEnabled()) {
202             logger.debug("Opening folder [" + MailTransportUtils.toPasswordProtectedString(storeUri) + "]");
203         }
204         folder.open(monitoringStrategy.getFolderOpenMode());
205     }
206 
207     private void closeFolder() {
208         MailTransportUtils.closeFolder(folder, true);
209     }
210 
211     private void closeSession() {
212         MailTransportUtils.closeService(store);
213     }
214 
215     private class MonitoringRunnable implements SchedulingAwareRunnable {
216 
217         public void run() {
218             try {
219                 openFolder();
220                 while (isRunning()) {
221                     try {
222                         Message[] messages = monitoringStrategy.monitor(folder);
223                         for (Message message : messages) {
224                             MessageHandler handler = new MessageHandler(message);
225                             execute(handler);
226                         }
227                     }
228                     catch (FolderClosedException ex) {
229                         logger.debug("Folder closed, reopening");
230                         if (isRunning()) {
231                             openFolder();
232                         }
233                     }
234                     catch (MessagingException ex) {
235                         logger.warn(ex);
236                     }
237                 }
238             }
239             catch (InterruptedException ex) {
240                 // Restore the interrupted status
241                 Thread.currentThread().interrupt();
242             }
243             catch (MessagingException ex) {
244                 logger.error(ex);
245             }
246         }
247 
248         public boolean isLongLived() {
249             return true;
250         }
251     }
252 
253     private class MessageHandler implements SchedulingAwareRunnable {
254 
255         private final Message message;
256 
257         public MessageHandler(Message message) {
258             this.message = message;
259         }
260 
261         public void run() {
262             MailReceiverConnection connection = new MailReceiverConnection(message, session);
263             connection.setTransportUri(transportUri);
264             connection.setFrom(from);
265             try {
266                 handleConnection(connection);
267             }
268             catch (Exception ex) {
269                 logger.error("Could not handle incoming mail connection", ex);
270             }
271         }
272 
273         public boolean isLongLived() {
274             return false;
275         }
276     }
277 
278 }