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