View Javadoc

1   /*
2    * $Id: AbstractMailConnector.java 20112 2010-11-08 01:33:08Z mike.schilling $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.transport.email;
12  
13  import org.mule.api.MuleContext;
14  import org.mule.api.MuleException;
15  import org.mule.api.endpoint.EndpointURI;
16  import org.mule.api.endpoint.ImmutableEndpoint;
17  import org.mule.api.lifecycle.InitialisationException;
18  import org.mule.transport.AbstractConnector;
19  import org.mule.util.PropertiesUtils;
20  import org.mule.util.StringUtils;
21  
22  import java.io.UnsupportedEncodingException;
23  import java.net.URLDecoder;
24  import java.util.Enumeration;
25  import java.util.HashMap;
26  import java.util.Map;
27  import java.util.Properties;
28  
29  import javax.mail.Authenticator;
30  import javax.mail.Session;
31  import javax.mail.URLName;
32  
33  /**
34   * Abstract superclass for mail connectors. Provides Mule with an Authenticator
35   * object and other shared functionality like e.g. MuleSession creation.
36   */
37  public abstract class AbstractMailConnector extends AbstractConnector
38  {
39      public static final String ATTACHMENT_HEADERS_PROPERTY_POSTFIX = "Headers";
40      public static final String MAILBOX = "INBOX";
41  
42      private Map<ImmutableEndpoint, SessionDetails> sessions = new HashMap<ImmutableEndpoint, SessionDetails>();
43      private String mailboxFolder;
44      private int defaultPort;
45  
46      /**
47       * A custom authenticator to be used on any mail sessions created with this
48       * connector. This will only be used if user name credendials are set on the
49       * endpoint.
50       */
51      private Authenticator authenticator = null;
52  
53      public AbstractMailConnector(int defaultPort, String mailboxFolder, MuleContext context)
54      {
55          super(context);
56          this.defaultPort = defaultPort;
57          this.mailboxFolder = mailboxFolder;
58      }
59  
60      public int getDefaultPort()
61      {
62          return defaultPort;
63      }
64  
65      public Authenticator getAuthenticator()
66      {
67          return authenticator;
68      }
69  
70      public void setAuthenticator(Authenticator authenticator)
71      {
72          this.authenticator = authenticator;
73      }
74      
75      public String getMailboxFolder()
76      {
77          return mailboxFolder;
78      }
79  
80      public void setMailboxFolder(String mailboxFolder)
81      {
82          this.mailboxFolder = mailboxFolder;
83      }
84  
85      public SessionDetails getSessionDetails(ImmutableEndpoint endpoint) throws UnsupportedEncodingException
86      {
87          // do not use this connector's implicit mutex by making this method synchronized. This
88          // may interfere with other methods using the same mutex for different purposes.
89          synchronized (sessions)
90          {
91              SessionDetails sessionDetails = sessions.get(endpoint);
92              if (null == sessionDetails)
93              {
94                  sessionDetails = newSession(endpoint);
95                  sessions.put(endpoint, sessionDetails);
96              }
97              return sessionDetails;
98          }
99      }
100     
101     public URLName urlFromEndpoint(ImmutableEndpoint endpoint) throws UnsupportedEncodingException
102     {
103         String inbox = endpoint.getEndpointURI().getPath();
104         if (inbox.length() == 0)
105         {
106             inbox = getMailboxFolder();
107         }
108         else
109         {
110             inbox = inbox.substring(1);
111         }
112 
113         EndpointURI uri = endpoint.getEndpointURI();
114         String user = uri.getUser();
115         if (user != null)
116         {
117             user = URLDecoder.decode(user, endpoint.getEncoding());
118         }
119         String pass = uri.getPassword();
120         if (pass != null)
121         {
122             pass = URLDecoder.decode(pass, endpoint.getEncoding());
123         }
124 
125         return new URLName(uri.getScheme(), uri.getHost(), uri.getPort(), inbox, user, pass);
126     }
127     
128     /**
129      * Some protocols (eg secure extensions) extend a "base" protocol.
130      * Subclasses for such protocols should override this method.
131      * 
132      * @return the underlying (eg non-secure) protocol
133      */
134     protected String getBaseProtocol()
135     {
136         return getProtocol();
137     }
138     
139     /**
140      * Subclasses should extend this to add further properties.  
141      * Synchronization is managed outside this call (so no need to synchronize further on properties)
142      * 
143      * @param global system properties 
144      * @param local local properties (specific to one session)
145      * @param url the endpoint url
146      */
147     protected void extendPropertiesForSession(Properties global, Properties local, URLName url)
148     {
149         int port = url.getPort();
150         if (port == -1)
151         {
152             port = this.getDefaultPort();
153         }
154         local.setProperty("mail." + getBaseProtocol() + ".socketFactory.port", Integer.toString(port));
155 
156         if (StringUtils.isNotBlank(url.getPassword()))
157         {
158             local.setProperty("mail." + getBaseProtocol() + ".auth", "true");
159             if (getAuthenticator() == null)
160             {
161                 setAuthenticator(new DefaultAuthenticator(url.getUsername(), url.getPassword()));
162                 if (logger.isDebugEnabled())
163                 {
164                     logger.debug("No Authenticator set on connector: " + getName() + "; using default.");
165                 }
166             }
167         }
168         else
169         {
170             local.setProperty("mail." + getBaseProtocol() + ".auth", "false");
171         }
172         
173         // TODO - i'm not at all certain that these properties (especially the ones
174         // using the base protocol) are needed.  they are inherited from old, gnarly
175         // code.
176 
177         if (StringUtils.isNotBlank(url.getHost())) {
178             local.setProperty("mail." + getBaseProtocol() + ".host", url.getHost());
179         }
180         local.setProperty("mail." + getBaseProtocol() + ".rsetbeforequit", "true");
181     }
182 
183     protected SessionDetails newSession(ImmutableEndpoint endpoint) throws UnsupportedEncodingException
184     {
185         URLName url = urlFromEndpoint(endpoint);
186 
187         Properties global = System.getProperties();
188         Properties local = new Properties();
189         //Allow properties to be set on the endpoint
190         PropertiesUtils.getPropertiesWithPrefix(endpoint.getProperties(), "mail.", local);
191         Session session;
192 
193         // make sure we do not mess with authentication set via system properties
194         synchronized (global)
195         {
196             extendPropertiesForSession(global, local, url);
197             session = Session.getInstance(local, getAuthenticator());
198         }
199 
200         if (logger.isDebugEnabled())
201         {
202             local.setProperty("mail.debug", "true");
203             
204             dumpProperties("MuleSession local properties", local, true);
205             dumpProperties("System global properties", global, true);
206             logger.debug("Creating mail session: host = " + url.getHost() + ", port = " + url.getPort()
207                 + ", user = " + url.getUsername() + ", pass = " + url.getPassword());
208         }
209 
210         return new SessionDetails(session, url);
211     }
212     
213     protected void dumpProperties(String title, Properties properties, boolean filter)
214     {
215         int skipped = 0;
216         logger.debug(title + " =============");
217         Enumeration keys = properties.keys();
218         while (keys.hasMoreElements())
219         {
220             String key = (String) keys.nextElement();
221             if (!filter || key.startsWith("mule.") || key.startsWith("mail.") || key.startsWith("javax."))
222             {
223                 String value = properties.getProperty(key);
224                 logger.debug(key + ": " + value);
225             }
226             else 
227             {
228                 ++skipped;
229             }
230         }
231         if (filter)
232         {
233             logger.debug("skipped " + skipped);
234         }
235     }
236     
237     // supply these here because sub-classes are very simple
238 
239     @Override
240     protected void doInitialise() throws InitialisationException
241     {
242         // template method, nothing to do
243     }
244 
245     @Override
246     protected void doDispose()
247     {
248         // template method, nothing to do
249     }
250 
251     @Override
252     protected void doConnect() throws Exception
253     {
254         // template method, nothing to do
255     }
256 
257     @Override
258     protected void doDisconnect() throws Exception
259     {
260         // template method, nothing to do
261     }
262 
263     @Override
264     protected void doStart() throws MuleException
265     {
266         // template method, nothing to do
267     }
268 
269     @Override
270     protected void doStop() throws MuleException
271     {
272         // template method, nothing to do
273     }
274 
275 }