View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.geronimo.javamail.store.pop3.connection;
19  
20  import java.util.ArrayList;
21  import java.util.Iterator;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.StringTokenizer;
25  
26  import javax.mail.MessagingException; 
27  import javax.mail.Session;
28  import javax.mail.Store;
29  
30  import javax.mail.StoreClosedException;
31  
32  import org.apache.geronimo.javamail.store.pop3.POP3Store; 
33  import org.apache.geronimo.javamail.util.ProtocolProperties; 
34  
35  public class POP3ConnectionPool {
36  
37      protected static final String MAIL_PORT = "port";
38      
39      protected static final String MAIL_SASL_REALM = "sasl.realm"; 
40      protected static final String MAIL_AUTHORIZATIONID = "sasl.authorizationid"; 
41  
42      protected static final String DEFAULT_MAIL_HOST = "localhost";
43  
44      // Our hosting Store instance
45      protected POP3Store store;
46      // our Protocol abstraction 
47      protected ProtocolProperties props; 
48      // POP3 is not nearly as multi-threaded as IMAP.  We really just have a single folder, 
49      // plus the Store, but the Store doesn't really talk to the server very much.  We only
50      // hold one connection available, and on the off chance there is a situation where 
51      // we need to create a new one, we'll authenticate on demand.  The one case where 
52      // I know this might be an issue is a folder checking back with the Store to see it if
53      // it is still connected.  
54      protected POP3Connection availableConnection;      
55      
56      // our debug flag
57      protected boolean debug;
58  
59      // the target host
60      protected String host;
61      // the target server port.
62      protected int port;
63      // the username we connect with
64      protected String username;
65      // the authentication password.
66      protected String password;
67      // the SASL realm name 
68      protected String realm; 
69      // the authorization id.  
70      protected String authid; 
71      // Turned on when the store is closed for business. 
72      protected boolean closed = false; 
73  
74      /**
75       * Create a connection pool associated with a give POP3Store instance.  The
76       * connection pool manages handing out connections for both the Store and
77       * Folder and Message usage.
78       * 
79       * @param store  The Store we're creating the pool for.
80       * @param props  The protocol properties abstraction we use.
81       */
82      public POP3ConnectionPool(POP3Store store, ProtocolProperties props) {
83          this.store = store;
84          this.props = props; 
85      }
86  
87  
88      /**
89       * Manage the initial connection to the POP3 server.  This is the first 
90       * point where we obtain the information needed to make an actual server 
91       * connection.  Like the Store protocolConnect method, we return false 
92       * if there's any sort of authentication difficulties. 
93       * 
94       * @param host     The host of the mail server.
95       * @param port     The mail server connection port.
96       * @param user     The connection user name.
97       * @param password The connection password.
98       * 
99       * @return True if we were able to connect and authenticate correctly. 
100      * @exception MessagingException
101      */
102     public synchronized boolean protocolConnect(String host, int port, String username, String password) throws MessagingException {
103         // NOTE:  We don't check for the username/password being null at this point.  It's possible that 
104         // the server will send back a PREAUTH response, which means we don't need to go through login 
105         // processing.  We'll need to check the capabilities response after we make the connection to decide 
106         // if logging in is necesssary. 
107         
108         // save this for subsequent connections.  All pool connections will use this info.
109         // if the port is defaulted, then see if we have something configured in the session.
110         // if not configured, we just use the default default.
111         if (port == -1) {
112             // check for a property and fall back on the default if it's not set.
113             port = props.getIntProperty(MAIL_PORT, props.getDefaultPort());
114             // it's possible that -1 might have been explicitly set, so one last check. 
115             if (port == -1) {
116                 port = props.getDefaultPort(); 
117             }
118         }
119     	
120     	// Before we do anything, let's make sure that we succesfully received a host
121     	if ( host == null ) {
122     		host = DEFAULT_MAIL_HOST;
123     	}
124         
125         this.host = host;
126         this.port = port;
127         this.username = username;
128         this.password = password;
129         
130         // make sure we have the realm information 
131         realm = props.getProperty(MAIL_SASL_REALM); 
132         // get an authzid value, if we have one.  The default is to use the username.
133         authid = props.getProperty(MAIL_AUTHORIZATIONID, username);
134 
135         // go create a connection and just add it to the pool.  If there is an authenticaton error, 
136         // return the connect failure, and we may end up trying again. 
137         availableConnection = createPoolConnection(); 
138         if (availableConnection == null) {
139             return false; 
140         }
141         // we're connected, authenticated, and ready to go. 
142         return true; 
143     }
144 
145     /**
146      * Creates an authenticated pool connection and adds it to
147      * the connection pool.  If there is an existing connection
148      * already in the pool, this returns without creating a new
149      * connection.
150      *
151      * @exception MessagingException
152      */
153     protected POP3Connection createPoolConnection() throws MessagingException {
154         POP3Connection connection = new POP3Connection(props);
155         if (!connection.protocolConnect(host, port, authid, realm, username, password)) {
156             // we only add live connections to the pool.  Sever the connections and 
157             // allow it to go free. 
158             connection.closeServerConnection(); 
159             return null; 
160         }
161         // just return this connection 
162         return connection; 
163     }
164 
165 
166     /**
167      * Get a connection from the pool.  We try to retrieve a live
168      * connection, but we test the connection's liveness before
169      * returning one.  If we don't have a viable connection in
170      * the pool, we'll create a new one.  The returned connection
171      * will be in the authenticated state already.
172      *
173      * @return A POP3Connection object that is connected to the server.
174      */
175     public synchronized POP3Connection getConnection() throws MessagingException {
176         // if we have an available one (common when opening the INBOX), just return it 
177         POP3Connection connection = availableConnection; 
178         
179         if (connection != null) {
180             availableConnection = null; 
181             return connection; 
182         }
183         // we need an additional connection...rare, but it can happen if we've closed the INBOX folder. 
184         return createPoolConnection(); 
185     }
186     
187     
188     /**
189      * Return a connection to the connection pool.
190      * 
191      * @param connection The connection getting returned.
192      * 
193      * @exception MessagingException
194      */
195     public synchronized void releaseConnection(POP3Connection connection) throws MessagingException
196     {
197         // we're generally only called if the store needed to talk to the server and 
198         // then returned the connection to the pool.  So it's pretty likely that we'll just cache this
199         if (availableConnection == null) {
200             availableConnection = connection; 
201         }
202         else {
203             // got too many connections created...not sure how, but get rid of this one. 
204             connection.close(); 
205         }
206     }
207     
208     
209     /**
210      * Close the entire connection pool. 
211      * 
212      * @exception MessagingException
213      */
214     public synchronized void close() throws MessagingException {
215         // we'll on have the single connection in reserver 
216         if (availableConnection != null) {
217             availableConnection.close(); 
218             availableConnection = null; 
219         }
220         // turn out the lights, hang the closed sign on the wall. 
221         closed = true; 
222     }
223 }
224