001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.geronimo.javamail.store.pop3.connection;
019    
020    import java.util.ArrayList;
021    import java.util.Iterator;
022    import java.util.List;
023    import java.util.Map;
024    import java.util.StringTokenizer;
025    
026    import javax.mail.MessagingException; 
027    import javax.mail.Session;
028    import javax.mail.Store;
029    
030    import javax.mail.StoreClosedException;
031    
032    import org.apache.geronimo.javamail.store.pop3.POP3Store; 
033    import org.apache.geronimo.javamail.util.ProtocolProperties; 
034    
035    public class POP3ConnectionPool {
036    
037        protected static final String MAIL_PORT = "port";
038        
039        protected static final String MAIL_SASL_REALM = "sasl.realm"; 
040        protected static final String MAIL_AUTHORIZATIONID = "sasl.authorizationid"; 
041    
042        protected static final String DEFAULT_MAIL_HOST = "localhost";
043    
044        // Our hosting Store instance
045        protected POP3Store store;
046        // our Protocol abstraction 
047        protected ProtocolProperties props; 
048        // POP3 is not nearly as multi-threaded as IMAP.  We really just have a single folder, 
049        // plus the Store, but the Store doesn't really talk to the server very much.  We only
050        // hold one connection available, and on the off chance there is a situation where 
051        // we need to create a new one, we'll authenticate on demand.  The one case where 
052        // I know this might be an issue is a folder checking back with the Store to see it if
053        // it is still connected.  
054        protected POP3Connection availableConnection;      
055        
056        // our debug flag
057        protected boolean debug;
058    
059        // the target host
060        protected String host;
061        // the target server port.
062        protected int port;
063        // the username we connect with
064        protected String username;
065        // the authentication password.
066        protected String password;
067        // the SASL realm name 
068        protected String realm; 
069        // the authorization id.  
070        protected String authid; 
071        // Turned on when the store is closed for business. 
072        protected boolean closed = false; 
073    
074        /**
075         * Create a connection pool associated with a give POP3Store instance.  The
076         * connection pool manages handing out connections for both the Store and
077         * Folder and Message usage.
078         * 
079         * @param store  The Store we're creating the pool for.
080         * @param props  The protocol properties abstraction we use.
081         */
082        public POP3ConnectionPool(POP3Store store, ProtocolProperties props) {
083            this.store = store;
084            this.props = props; 
085        }
086    
087    
088        /**
089         * Manage the initial connection to the POP3 server.  This is the first 
090         * point where we obtain the information needed to make an actual server 
091         * connection.  Like the Store protocolConnect method, we return false 
092         * if there's any sort of authentication difficulties. 
093         * 
094         * @param host     The host of the mail server.
095         * @param port     The mail server connection port.
096         * @param user     The connection user name.
097         * @param password The connection password.
098         * 
099         * @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