funkman 2003/08/25 18:10:20 Modified: catalina/src/share/org/apache/catalina/realm JDBCRealm.java Log: Big JDBCRealm cleanup 7116 - JDBC realm doesn't handle NULL passwords 10623 - JDBCRealm lacks one DB commit, preventing sucessfull authentication under certain circunstances 11929 - In case db connection is bad (stale due to firewall ...) - retry authenticating (2 tries total) And many dups 8091 - Allow tomcat to startup even if the database isn't available (and some other dup bz items) Make authenticate synchronized since there are race conditions between the connection being opened, used and exceptions occuring. Revision Changes Path 1.2 +107 -81 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/realm/JDBCRealm.java Index: JDBCRealm.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/realm/JDBCRealm.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- JDBCRealm.java 18 Jul 2002 16:47:55 -0000 1.1 +++ JDBCRealm.java 26 Aug 2003 01:10:20 -0000 1.2 @@ -65,7 +65,6 @@ import java.io.File; -import java.security.MessageDigest; import java.security.Principal; import java.sql.Connection; import java.sql.Driver; @@ -74,17 +73,8 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.Properties; -import org.apache.catalina.Container; -import org.apache.catalina.Lifecycle; -import org.apache.catalina.LifecycleEvent; import org.apache.catalina.LifecycleException; -import org.apache.catalina.LifecycleListener; -import org.apache.catalina.Logger; -import org.apache.catalina.Realm; -import org.apache.catalina.util.HexUtils; -import org.apache.catalina.util.LifecycleSupport; import org.apache.catalina.util.StringManager; -import org.apache.catalina.util.Base64; /** @@ -95,7 +85,7 @@ * * <p><strong>TODO</strong> - Support connection pooling (including message * format objects) so that <code>authenticate()</code> does not have to be -* synchronized.</p> +* synchronized and would fix the ugly connection logic. </p> * * @author Craig R. McClanahan * @author Carson McDonald @@ -377,43 +367,53 @@ * event is also logged, and the connection will be closed so that * a subsequent request will automatically re-open it. * + * * @param username Username of the Principal to look up * @param credentials Password or other credentials to use in * authenticating this username */ - public Principal authenticate(String username, String credentials) { - - Connection dbConnection = null; + public synchronized Principal authenticate(String username, String credentials) { - try { + // Number of tries is the numebr of attempts to connect to the database + // during this login attempt (if we need to open the database) + // This needs rewritten wuth better pooling support, the existing code + // needs signature changes since the Prepared statements needs cached + // with the connections. + // The code below will try twice if there is a SQLException so the + // connection may try to be opened again. On normal conditions (including + // invalid login - the above is only used once. + int numberOfTries = 2; + while (numberOfTries>0) { + try { - // Ensure that we have an open database connection - dbConnection = open(); + // Ensure that we have an open database connection + open(); - // Acquire a Principal object for this user - Principal principal = authenticate(dbConnection, - username, credentials); + // Acquire a Principal object for this user + Principal principal = authenticate(dbConnection, + username, credentials); - // Release the database connection we just used - release(dbConnection); - // Return the Principal (if any) - return (principal); + // Return the Principal (if any) + return (principal); - } catch (SQLException e) { + } catch (SQLException e) { - // Log the problem for posterity - log(sm.getString("jdbcRealm.exception"), e); + // Log the problem for posterity + log(sm.getString("jdbcRealm.exception"), e); - // Close the connection so that it gets reopened next time - if (dbConnection != null) - close(dbConnection); + // Close the connection so that it gets reopened next time + if (dbConnection != null) + close(dbConnection); - // Return "not authenticated" for this request - return (null); + } + numberOfTries--; } + // Worst case scenario + return null; + } @@ -441,47 +441,70 @@ // Look up the user's credentials String dbCredentials = null; - PreparedStatement stmt = credentials(dbConnection, username); - ResultSet rs = stmt.executeQuery(); - while (rs.next()) { - dbCredentials = rs.getString(1).trim(); - } - rs.close(); - if (dbCredentials == null) { - return (null); - } + PreparedStatement stmt = null; + ResultSet rs = null; - // Validate the user's credentials - boolean validated = false; - if (hasMessageDigest()) { - // Hex hashes should be compared case-insensitive - validated = (digest(credentials).equalsIgnoreCase(dbCredentials)); - } else - validated = (digest(credentials).equals(dbCredentials)); - - if (validated) { - if (debug >= 2) - log(sm.getString("jdbcRealm.authenticateSuccess", - username)); - } else { - if (debug >= 2) - log(sm.getString("jdbcRealm.authenticateFailure", - username)); - return (null); - } + try { + stmt = credentials(dbConnection, username); + rs = stmt.executeQuery(); + + if (rs.next()) { + dbCredentials = rs.getString(1); + } + rs.close(); + rs = null; + if (dbCredentials == null) { + return (null); + } + + dbCredentials = dbCredentials.trim(); - // Accumulate the user's roles - ArrayList list = new ArrayList(); - stmt = roles(dbConnection, username); - rs = stmt.executeQuery(); - while (rs.next()) { - list.add(rs.getString(1).trim()); - } - rs.close(); - dbConnection.commit(); - // Create and return a suitable Principal for this user - return (new GenericPrincipal(this, username, credentials, list)); + // Validate the user's credentials + boolean validated = false; + if (hasMessageDigest()) { + // Hex hashes should be compared case-insensitive + validated = (digest(credentials).equalsIgnoreCase(dbCredentials)); + } else { + validated = (digest(credentials).equals(dbCredentials)); + } + + if (validated) { + if (debug >= 2) + log(sm.getString("jdbcRealm.authenticateSuccess", + username)); + } else { + if (debug >= 2) + log(sm.getString("jdbcRealm.authenticateFailure", + username)); + return (null); + } + + // Accumulate the user's roles + ArrayList roleList = new ArrayList(); + stmt = roles(dbConnection, username); + rs = stmt.executeQuery(); + while (rs.next()) { + String role = rs.getString(1); + if (null!=role) { + roleList.add(role.trim()); + } + } + rs.close(); + rs = null; + + // Create and return a suitable Principal for this user + return (new GenericPrincipal(this, username, credentials, roleList)); + } finally { + if (rs!=null) { + try { + rs.close(); + } catch(SQLException e) { + log(sm.getString("jdbcRealm.abnormalCloseResultSet")); + } + } + dbConnection.commit(); + } } @@ -503,24 +526,26 @@ } catch (Throwable f) { ; } + this.preparedCredentials = null; + + try { preparedRoles.close(); } catch (Throwable f) { ; } + this.preparedRoles = null; + // Close this database connection, and log any errors try { dbConnection.close(); } catch (SQLException e) { log(sm.getString("jdbcRealm.close"), e); // Just log it here + } finally { + this.dbConnection = null; } - // Release resources associated with the closed connection - this.dbConnection = null; - this.preparedCredentials = null; - this.preparedRoles = null; - } @@ -674,11 +699,12 @@ */ public void start() throws LifecycleException { - // Validate that we can open our connection + // Validate that we can open our connection - but let tomcat + // startup in case the database is temporarily unavailable try { open(); } catch (SQLException e) { - throw new LifecycleException(sm.getString("jdbcRealm.open"), e); + log(sm.getString("jdbcRealm.open"), e); } // Perform normal superclass initialization
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]