View Javadoc

1   /*
2    * Copyright 2007 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 reponse 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:[email protected]/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:[email protected]</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     public void afterPropertiesSet() throws Exception {
135         Assert.notNull(storeUri, "Property 'storeUri' is required");
136         Assert.notNull(transportUri, "Property 'transportUri' is required");
137         if (monitoringStrategy == null) {
138             String protocol = storeUri.getProtocol();
139             if ("pop3".equals(protocol)) {
140                 monitoringStrategy = new Pop3PollingMonitoringStrategy();
141             }
142             else if ("imap".equals(protocol)) {
143                 monitoringStrategy = new PollingMonitoringStrategy();
144             }
145             else {
146                 throw new IllegalArgumentException("Cannot determine monitoring strategy for \"" + protocol + "\". " +
147                         "Set the 'monitoringStrategy' explicitly.");
148             }
149         }
150         super.afterPropertiesSet();
151     }
152 
153     protected void onActivate() throws MessagingException {
154         openSession();
155         openFolder();
156     }
157 
158     protected void onStart() {
159         if (logger.isInfoEnabled()) {
160             logger.info("Starting mail receiver [" + MailTransportUtils.toPasswordProtectedString(storeUri) + "]");
161         }
162         execute(new MonitoringRunnable());
163     }
164 
165     protected void onStop() {
166         if (logger.isInfoEnabled()) {
167             logger.info("Stopping mail receiver [" + MailTransportUtils.toPasswordProtectedString(storeUri) + "]");
168         }
169         closeFolder();
170     }
171 
172     protected void onShutdown() {
173         if (logger.isInfoEnabled()) {
174             logger.info("Shutting down mail receiver [" + MailTransportUtils.toPasswordProtectedString(storeUri) + "]");
175         }
176         closeFolder();
177         closeSession();
178     }
179 
180     private void openSession() throws MessagingException {
181         store = session.getStore(storeUri);
182         if (logger.isDebugEnabled()) {
183             logger.debug("Connecting to store [" + MailTransportUtils.toPasswordProtectedString(storeUri) + "]");
184         }
185         store.connect();
186     }
187 
188     private void openFolder() throws MessagingException {
189         if (folder != null && folder.isOpen()) {
190             return;
191         }
192         folder = store.getFolder(storeUri);
193         if (folder == null || !folder.exists()) {
194             throw new IllegalStateException("No default folder to receive from");
195         }
196         if (logger.isDebugEnabled()) {
197             logger.debug("Opening folder [" + MailTransportUtils.toPasswordProtectedString(storeUri) + "]");
198         }
199         folder.open(monitoringStrategy.getFolderOpenMode());
200     }
201 
202     private void closeFolder() {
203         MailTransportUtils.closeFolder(folder, true);
204     }
205 
206     private void closeSession() {
207         MailTransportUtils.closeService(store);
208     }
209 
210     private class MonitoringRunnable implements SchedulingAwareRunnable {
211 
212         public void run() {
213             try {
214                 openFolder();
215                 while (isRunning()) {
216                     try {
217                         Message[] messages = monitoringStrategy.monitor(folder);
218                         for (int i = 0; i < messages.length; i++) {
219                             MessageHandler handler = new MessageHandler(messages[i]);
220                             execute(handler);
221                         }
222                     }
223                     catch (FolderClosedException ex) {
224                         logger.debug("Folder closed, reopening");
225                         if (isRunning()) {
226                             openFolder();
227                         }
228                     }
229                     catch (MessagingException ex) {
230                         logger.warn(ex);
231                     }
232                 }
233             }
234             catch (InterruptedException ex) {
235                 // Restore the interrupted status
236                 Thread.currentThread().interrupt();
237             }
238             catch (MessagingException ex) {
239                 logger.error(ex);
240             }
241         }
242 
243         public boolean isLongLived() {
244             return true;
245         }
246     }
247 
248     private class MessageHandler implements SchedulingAwareRunnable {
249 
250         private final Message message;
251 
252         public MessageHandler(Message message) {
253             this.message = message;
254         }
255 
256         public void run() {
257             MailReceiverConnection connection = new MailReceiverConnection(message, session);
258             connection.setTransportUri(transportUri);
259             connection.setFrom(from);
260             try {
261                 handleConnection(connection);
262             }
263             catch (Exception ex) {
264                 logger.error("Could not handle incoming mail connection", ex);
265             }
266         }
267 
268         public boolean isLongLived() {
269             return false;
270         }
271     }
272 
273 }