diff --git a/projects/samskivert/src/java/com/samskivert/io/PersistenceException.java b/projects/samskivert/src/java/com/samskivert/io/PersistenceException.java new file mode 100644 index 00000000..457ead78 --- /dev/null +++ b/projects/samskivert/src/java/com/samskivert/io/PersistenceException.java @@ -0,0 +1,60 @@ +// +// $Id: PersistenceException.java,v 1.1 2001/09/20 01:53:19 mdb Exp $ +// +// samskivert library - useful routines for java programs +// Copyright (C) 2001 Michael Bayne +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation; either version 2.1 of the License, or +// (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.samskivert.jdbc; + +import org.apache.commons.util.exception.NestableException; + +/** + * A persistence exception can be thrown when an error occurs in the + * underlying persistence code. By encapsulating errors, one retains the + * ability to make changes to the implementation structure without + * affecting the interface to persistence services presented to the + * application. + */ +public class PersistenceException extends NestableException +{ + /** + * Constructs a persistence exception with the specified error + * message. + */ + public PersistenceException (String message) + { + super(message); + } + + /** + * Constructs a persistence exception with the specified error message + * and the chained causing event. + */ + public PersistenceException (String message, Exception cause) + { + super(message, cause); + } + + /** + * Constructs a persistence exception with the specified chained + * causing event. + */ + public PersistenceException (Exception cause) + { + super(cause); + } +} diff --git a/projects/samskivert/src/java/com/samskivert/jdbc/ConnectionProvider.java b/projects/samskivert/src/java/com/samskivert/jdbc/ConnectionProvider.java new file mode 100644 index 00000000..bc7652d6 --- /dev/null +++ b/projects/samskivert/src/java/com/samskivert/jdbc/ConnectionProvider.java @@ -0,0 +1,84 @@ +// +// $Id: ConnectionProvider.java,v 1.1 2001/09/20 01:53:19 mdb Exp $ +// +// samskivert library - useful routines for java programs +// Copyright (C) 2001 Michael Bayne +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation; either version 2.1 of the License, or +// (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.samskivert.jdbc; + +import java.sql.*; + +/** + * As the repository aims to interface with whatever database pooling + * services a project cares to use, it obtains all of its database + * connections through a connection provider. The connection provider + * provides connections based on a database identifier (a string) which + * identifies a particular connection to a particular database. A user of + * these services would then coordinate the database identifier (or + * identifiers) used to invoke a database operation with the database + * connections that are returned by the connection provider that they + * provided to the repository at construct time. + */ +public interface ConnectionProvider +{ + /** + * Obtains a database connection based on the supplied database + * identifier. The repository expects to have exclusive use of this + * connection instance until it releases it. This connection will be + * released subsequently with a call to {@link #releaseConnection} or + * {@link #connectionFailed} depending on the circumstances of the + * release. close() will not be called on the + * connection. It is up to the connection provider to close the + * connection when it is released if appropriate. + * + * @param ident the database connection identifier. + * + * @return an active JDBC connection (which may have come from a + * connection pool). + * + * @exception PersistenceException thrown if a problem occurs trying + * to open the requested connection. + */ + public Connection getConnection (String ident) + throws PersistenceException; + + /** + * Releases a database connection when it is no longer needed by the + * repository. + * + * @param ident the database identifier used when obtaining this + * connection. + * @param conn the connection to release (back into the pool or to be + * closed if pooling is not going on). + */ + public void releaseConnection (String ident, Connection conn); + + /** + * Called by the repository if a failure occurred on the connection. + * It is expected that the connection will be disposed of and + * subsequent calls to getConnection will return a + * freshly established connection to the database. + * + * @param ident the database identifier used when obtaining this + * connection. + * @param conn the connection that failed. + * @param error the error thrown by the connection (which may be used + * to determine if the connection should be closed or can be reused). + */ + public void connectionFailed (String ident, Connection conn, + SQLException error); +} diff --git a/projects/samskivert/src/java/com/samskivert/jdbc/DatabaseLiaison.java b/projects/samskivert/src/java/com/samskivert/jdbc/DatabaseLiaison.java new file mode 100644 index 00000000..fa0dc87b --- /dev/null +++ b/projects/samskivert/src/java/com/samskivert/jdbc/DatabaseLiaison.java @@ -0,0 +1,74 @@ +// +// $Id: DatabaseLiaison.java,v 1.1 2001/09/20 01:53:19 mdb Exp $ +// +// samskivert library - useful routines for java programs +// Copyright (C) 2001 Michael Bayne +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation; either version 2.1 of the License, or +// (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.samskivert.jdbc; + +import java.sql.Connection; +import java.sql.SQLException; + +/** + * Despite good intentions, JDBC and SQL do not provide a unified + * interface to all databases. There remain idiosyncrasies that must be + * worked around when making code interact with different database + * servers. The database liaison encapsulates the code needed to + * straighten out the curves and curve out the straights. + */ +public interface DatabaseLiaison +{ + /** + * Indicates whether this particular RDBMS/JDBC driver combination + * supports transactions. + * + * @return true if transactions are supported, false if not. + */ + public boolean supportsTransactions (); + + /** + * Determines whether or not the supplied SQL exception was caused by + * a duplicate row being inserted into a table with a unique key. + * + * @return true if the exception was caused by the insertion of a + * duplicate row, false if not. + */ + public boolean isDuplicateRowException (SQLException sqe); + + /** + * Determines whether or not the supplied SQL exception is a transient + * failure, meaning one that is not related to the SQL being executed, + * but instead to the environment at the time of execution, like the + * connection to the database having been lost. + * + * @return true if the exception was thrown due to a transient + * failure, false if not. + */ + public boolean isTransientException (SQLException sqe); + + /** + * Returns the value of an AUTO_INCREMENT column for the + * last row insertion. This is MySQL specific, but exists here for now + * until we sort out a general purpose mechanism for assigning unique + * ids. + * + * @return the most recent ID generated by an insert into an + * AUTO_INCREMENT table or -1 if it could not be + * obtained. + */ + public int lastInsertedId (Connection conn) throws SQLException; +} diff --git a/projects/samskivert/src/java/com/samskivert/jdbc/JORARepository.java b/projects/samskivert/src/java/com/samskivert/jdbc/JORARepository.java new file mode 100644 index 00000000..b159bb42 --- /dev/null +++ b/projects/samskivert/src/java/com/samskivert/jdbc/JORARepository.java @@ -0,0 +1,73 @@ +// +// $Id: JORARepository.java,v 1.1 2001/09/20 01:53:19 mdb Exp $ +// +// samskivert library - useful routines for java programs +// Copyright (C) 2001 Michael Bayne +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation; either version 2.1 of the License, or +// (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.samskivert.jdbc; + +import java.sql.*; +import java.util.Properties; + +import com.samskivert.jdbc.jora.*; + +/** + * The JORA repository simplifies the process of building persistence + * services that make use of the JORA object relational mapping package. + * + * @see com.samskivert.jdbc.jora.Session + */ +public abstract class JORARepository extends SimpleRepository +{ + /** + * Creates and initializes a JORA repository which will access the + * database identified by the supplied database identifier. + * + * @param provider the connection provider which will be used to + * obtain our database connection. + * @param dbident the identifier of the database that will be accessed + * by this repository. + */ + public JORARepository (ConnectionProvider provider, String dbident) + { + super(provider, dbident); + + // create our JORA session + _session = new Session((Connection)null); + + // create our tables + createTables(_session); + } + + /** + * After the database session is begun, this function will be called + * to give the repository implementation the opportunity to create its + * table objects. + * + * @param session the session instance to use when creating your table + * instances. + */ + protected abstract void createTables (Session session); + + protected void gotConnection (Connection conn) + { + // let our session know about this connection + _session.setConnection(conn); + } + + protected Session _session; +} diff --git a/projects/samskivert/src/java/com/samskivert/jdbc/LiaisonRegistry.java b/projects/samskivert/src/java/com/samskivert/jdbc/LiaisonRegistry.java new file mode 100644 index 00000000..d8615738 --- /dev/null +++ b/projects/samskivert/src/java/com/samskivert/jdbc/LiaisonRegistry.java @@ -0,0 +1,39 @@ +// +// $Id: LiaisonRegistry.java,v 1.1 2001/09/20 01:53:19 mdb Exp $ +// +// samskivert library - useful routines for java programs +// Copyright (C) 2001 Michael Bayne +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation; either version 2.1 of the License, or +// (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.samskivert.jdbc; + +import java.sql.*; + +/** + * The liaison registry provides access to the appropriate database + * liaison implementation for a particular database connection. + */ +public class LiaisonRegistry +{ + /** + * Fetch the appropriate database liaison for the supplied database + * connection. + */ + public static DatabaseLiaison getLiaison (Connection conn) + { + return null; + } +} diff --git a/projects/samskivert/src/java/com/samskivert/jdbc/MySQLLiaison.java b/projects/samskivert/src/java/com/samskivert/jdbc/MySQLLiaison.java new file mode 100644 index 00000000..18cda34c --- /dev/null +++ b/projects/samskivert/src/java/com/samskivert/jdbc/MySQLLiaison.java @@ -0,0 +1,64 @@ +// +// $Id: MySQLLiaison.java,v 1.1 2001/09/20 01:53:19 mdb Exp $ +// +// samskivert library - useful routines for java programs +// Copyright (C) 2001 Michael Bayne +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation; either version 2.1 of the License, or +// (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.samskivert.jdbc; + +import java.sql.*; + +/** + * A database liaison for the MySQL database. + */ +public class MySQLLiaison +{ + // documentation inherited + public boolean supportsTransactions () + { + return false; + } + + // documentation inherited + public boolean isDuplicateRowException (SQLException sqe) + { + String msg = sqe.getMessage(); + return (msg != null && msg.indexOf("Duplicate entry") != -1); + } + + // documentation inherited + public boolean isTransientException (SQLException sqe) + { + String msg = sqe.getMessage(); + return (msg != null && + (msg.indexOf("Lost connection") != -1 || + msg.indexOf("Broken pipe") != -1)); + } + + // documentation inherited + public int lastInsertedId (Connection conn) throws SQLException + { + // we have to do this by hand. alas all is not roses. + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery("select LAST_INSERT_ID()"); + if (rs.next()) { + return rs.getInt(1); + } else { + return -1; + } + } +} diff --git a/projects/samskivert/src/java/com/samskivert/jdbc/MySQLRepository.java b/projects/samskivert/src/java/com/samskivert/jdbc/MySQLRepository.java deleted file mode 100644 index b818cca8..00000000 --- a/projects/samskivert/src/java/com/samskivert/jdbc/MySQLRepository.java +++ /dev/null @@ -1,92 +0,0 @@ -// -// $Id: MySQLRepository.java,v 1.8 2001/08/11 22:43:28 mdb Exp $ -// -// samskivert library - useful routines for java programs -// Copyright (C) 2001 Michael Bayne -// -// This library is free software; you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation; either version 2.1 of the License, or -// (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -package com.samskivert.jdbc; - -import java.sql.*; -import java.util.Properties; - -/** - * The MySQL repository class provides functionality useful to repository - * implementations that make use of MySQL as their underlying database. - */ -public abstract class MySQLRepository extends Repository -{ - /** - * Constructs a MySQL repository implementation with the supplied - * configuration properties. - */ - public MySQLRepository (Properties props) - throws SQLException - { - super(props); - } - - /** - * @return the most recent ID generated by an insert into an - * AUTO_INCREMENT table. - */ - protected int lastInsertedId () - throws SQLException - { - // make sure we've got a connection - ensureConnection(); - - // we have to do this by hand. alas all is not roses. - Statement stmt = _session.connection.createStatement(); - ResultSet rs = stmt.executeQuery("select LAST_INSERT_ID()"); - if (rs.next()) { - return rs.getInt(1); - } else { - return -1; - } - } - - /** - * Determines whether or not the supplied SQL exception originated - * from a duplicate row error. - * - * @return true if the exception was thrown because a duplicate row - * was inserted into a table that does not allow such things, false if - * the exception is not related to duplicate rows. - */ - protected boolean isDuplicateRowException (SQLException sqe) - { - String msg = sqe.getMessage(); - return (msg != null && msg.indexOf("Duplicate entry") != -1); - } - - /** - * Determines whether or not the supplied SQL exception is a transient - * failure, meaning one that is not related to the SQL being executed, - * but instead to the environment at the time of execution, like the - * connection to the database having been lost. - * - * @return true if the exception was thrown due to a transient - * failure, false if not. - */ - protected boolean isTransientException (SQLException sqe) - { - String msg = sqe.getMessage(); - return (msg != null && - (msg.indexOf("Lost connection") != -1 || - msg.indexOf("Broken pipe") != -1)); - } -} diff --git a/projects/samskivert/src/java/com/samskivert/jdbc/Repository.java b/projects/samskivert/src/java/com/samskivert/jdbc/Repository.java index 7836b61d..5cbbadc6 100644 --- a/projects/samskivert/src/java/com/samskivert/jdbc/Repository.java +++ b/projects/samskivert/src/java/com/samskivert/jdbc/Repository.java @@ -1,5 +1,5 @@ // -// $Id: Repository.java,v 1.9 2001/08/11 22:43:28 mdb Exp $ +// $Id: Repository.java,v 1.10 2001/09/20 01:53:19 mdb Exp $ // // samskivert library - useful routines for java programs // Copyright (C) 2001 Michael Bayne @@ -20,295 +20,98 @@ package com.samskivert.jdbc; -import java.sql.*; -import java.util.List; -import java.util.Properties; - -import com.samskivert.Log; -import com.samskivert.util.*; -import com.samskivert.jdbc.jora.*; +import java.sql.Connection; +import java.sql.SQLException; /** * The repository class provides basic functionality upon which to build * an interface to a repository of information stored in a database (a - * table or set of tables). + * table or set of tables) that is accessed via JDBC. * - *

These services are based on the JORA Java/RDBMS interoperability + *

It is expected that the repository class will encapsulate all + * database access for a particular table or set of tables. The interface + * provided to the rest of the application will involve only the + * application object model. For example: + * + *

+ * public class PeopleRepository extends SimpleRepository
+ * {
+ *     public Person getPerson (int personid);
+ *     public Person[] getPeopleByFirstName (String firstName);
+ *     public void updatePerson (Person person);
+ * }
+ * 
+ * + *

It is probably also desirable to catch SQL exceptions and wrap them + * in the PersistenceException class that is provided by this * package. + * + *

The repository comes in a few flavors depending on the needs of the + * persistence services being developed: + * + *

*/ -public abstract class Repository +public class Repository { /** - * Creates and initializes the repository. A properties object should - * be supplied with the following fields: + * Creates and initializes the repository. * - *
-     * driver=[jdbc driver class]
-     * url=[jdbc driver url]
-     * username=[jdbc username]
-     * password=[jdbc password]
-     * 
- * - *

The connection to the database will not be established until - * the first operation is executed. It is important that database - * operations be invoked via execute() rather than - * directly so that the repository can manage the database session - * properly (reestablishing the session on transient failures and so - * on). If a derived class wishes to perform operations directly, it - * should call ensureConnection() before performing any - * database activity. - * - * @param props a properties object containing the configuration - * parameters for the repository. + * @param provider the connection provider which will be used to + * obtain our database connection. */ - public Repository (Properties props) - throws SQLException + public Repository (ConnectionProvider provider) { - // extract our configuration parameters - _dclass = - requireProp(props, "driver", "No driver class specified."); - _url = - requireProp(props, "url", "No driver url specified."); - _username = - requireProp(props, "username", "No driver username specified."); - _password = - requireProp(props, "password", "No driver password specified."); - - // we attempt to reestablish the connection if there's a transient - // failure (like the database is down or our old connection gets - // closed by the database), but we don't want to get too crazy - // about connecting to the database automatically, so we limit our - // attempts to twice every thirty seconds - _throttle = new Throttle(2, 30); - } - - /** Establishes a connection to the database. */ - protected synchronized void ensureConnection () - throws SQLException - { - // nothing doing if we've already got a connection - if (_session != null) { - return; - } - - // make sure we don't try to reestablish the connection - // overzealously in times of lengthy database outage - if (_throttle.throttleOp()) { - throw new SQLException("Connection attempt throttled."); - } - - try { - // create our JORA session - _session = new Session(_dclass); - - // connect the session to the database. the only reason - // session.open() fails is class not found - if (!_session.open(_url, _username, _password)) { - throw new SQLException("Unable to load JDBC driver class: " + - _dclass); - } - - // set auto-commit to false - if (supportsTransactions()) { - _session.connection.setAutoCommit(false); - } - - // create our table objects - createTables(); - - } catch (SQLException sqe) { - // if we have any problems establishing a connection, close - // and clear out our session reference to prevent attempted - // use of a half initialized session - try { - shutdown(); - } catch (SQLException sqe2) { - Log.warning("Error shutting down half-initialized " + - "connection [primary_failure=" + sqe + - ", secondary_failure=" + sqe2 + "]."); - } - throw sqe; - } + _provider = provider; } /** - * Shuts down the existing connection to the database and - * reestablishes the connection. - */ - public void reestablishConnection () - throws SQLException - { - // first close the old connection - shutdown(); - // then open a new one - ensureConnection(); - } - - /** - * After the database session is begun, this function will be called - * to give the repository implementation the opportunity to create its - * table objects. If the database session fails, a new session will be - * established and this function will be called again for the new - * session. - */ - protected abstract void createTables () throws SQLException; - - protected static String requireProp (Properties props, - String name, String errmsg) - throws SQLException - { - String value = props.getProperty(name); - if (StringUtil.blank(value)) { - throw new SQLException(errmsg); - } - return value; - } - - /** - * A repository should be shutdown before the calling code disposes of - * it. This allows the repository to cleanly terminate the underlying - * database services. - */ - public synchronized void shutdown () - throws SQLException - { - if (_session != null) { - _session.close(); - _session = null; - } - } - - /** - * Used by execute. - * - * @see #execute + * Database operations should be encapsulated in instances of this + * class and then provided to the repository for invocation. This + * allows the repository to manage transaction commits for you as well + * as for it to automatically retry an operation if the connection + * failed for some transient reason. */ protected interface Operation { - public Object invoke () throws SQLException; + /** + * Invokes code that performs one or more database operations, all + * of which will be encapsulated in a single transaction (which + * can be retried in the event of a transient failure). + * + * @param conn the database connection on which the operations + * will be performed. + * @param liaison a database liaison for the supplied connection + * which can be used to determine things for which there is no + * standard way to determine via JDBC. + * + * @exception SQLException if thrown, this will be wrapped in a + * {@link PersistenceException} before being passed up to the + * operation invoker. + * @exception PersistenceException can be thrown if something goes + * awry when executing the operation. Note that the operation will + * not be retried if a persistence exception is thrown. Such + * exceptions are assumed to be application specific and not + * indicative of a basic JDBC failure. The transaction + * will be rolled back in such cases, however. + */ + public Object invoke (Connection conn, DatabaseLiaison liaison) + throws SQLException, PersistenceException; } - /** - * Executes the supplied operation. In the event of a transient - * failure, the repository will attempt to reestablish the database - * connection and try the operation again. - * - * @return whatever value is returned by the invoked operation. - */ - protected Object execute (Operation op) - throws SQLException - { - return execute(op, true); - } - - /** - * Executes the supplied operation followed by a call to commit() on - * the session unless an SQLException or runtime error occurs, in - * which case a call to rollback() is executed on the session. - * - * @param retryOnTransientFailure if true and the operation fails due - * to a transient failure (like losing the connection to the database - * or deadlock detection), the connection to the database will be - * reestablished and the operation attempted once more. - * - * @return whatever value is returned by the invoked operation. - */ - protected Object execute (Operation op, boolean retryOnTransientFailure) - throws SQLException - { - Object rv = null; - - // make sure our connection is established - ensureConnection(); - - try { - // invoke the operation - rv = op.invoke(); - - // commit the session - if (supportsTransactions()) { - _session.commit(); - } - - // return the operation result - return rv; - - } catch (SQLException sqe) { - // back out our changes if something got hosed - if (supportsTransactions()) { - try { - _session.rollback(); - } catch (SQLException rbe) { - Log.warning("Unable to roll back operation."); - Log.logStackTrace(rbe); - } - } - - // if this is a transient failure and we've been requested to - // retry such failures, close and reopen the database - // connection and try one more time - if (retryOnTransientFailure && isTransientException(sqe)) { - Log.info("Transient failure executing operation, " + - "retrying [error=" + sqe + "]."); - // to reset the connection, we simply close it. it will be - // reopened on our subsequent call to execute() - shutdown(); - return execute(op, false); - - } else { - Log.info("Non-transient exception, we're hosed " + - "[error=" + sqe + "]."); - throw sqe; - } - - } catch (RuntimeException rte) { - // back out our changes if something got hosed - if (supportsTransactions()) { - try { - _session.rollback(); - } catch (SQLException rbe) { - Log.warning("Unable to roll back operation."); - Log.logStackTrace(rbe); - } - } - throw rte; - } - } - - /** - * Specializations of this class for particular RDBMS/JDBC driver - * combinations should override this method and indicate whether or - * not transactions are supported. - */ - protected boolean supportsTransactions () - { - return false; - } - - /** - * Determines whether or not the supplied SQL exception is a transient - * failure, meaning one that is not related to the SQL being executed, - * but instead to the environment at the time of execution, like the - * connection to the database having been lost. - * - *

Specializations of the repository class for particular - * database/JDBC driver combos should override this function and match - * the appropriate exceptions. - * - * @return true if the exception was thrown due to a transient - * failure, false if not. - */ - protected boolean isTransientException (SQLException sqe) - { - return false; - } - - protected Session _session; - protected String _dclass; - protected String _url; - protected String _username; - protected String _password; - - // we use this to prevent too many database connection attempts - protected Throttle _throttle; + /** Our database connection provider. */ + protected ConnectionProvider _provider; } diff --git a/projects/samskivert/src/java/com/samskivert/jdbc/SimpleRepository.java b/projects/samskivert/src/java/com/samskivert/jdbc/SimpleRepository.java new file mode 100644 index 00000000..0dc2de64 --- /dev/null +++ b/projects/samskivert/src/java/com/samskivert/jdbc/SimpleRepository.java @@ -0,0 +1,177 @@ +// +// $Id: SimpleRepository.java,v 1.1 2001/09/20 01:53:20 mdb Exp $ +// +// samskivert library - useful routines for java programs +// Copyright (C) 2001 Michael Bayne +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation; either version 2.1 of the License, or +// (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.samskivert.jdbc; + +import java.sql.*; +import java.util.Properties; + +import com.samskivert.Log; + +/** + * The simple repository should be used for a repository that only needs + * access to a single JDBC connection instance to perform its persistence + * services. + */ +public class SimpleRepository extends Repository +{ + /** + * Creates and initializes a simple repository which will access the + * database identified by the supplied database identifier. + * + * @param provider the connection provider which will be used to + * obtain our database connection. + * @param dbident the identifier of the database that will be accessed + * by this repository. + */ + public SimpleRepository (ConnectionProvider provider, String dbident) + { + super(provider); + _dbident = dbident; + } + + /** + * Executes the supplied operation. In the event of a transient + * failure, the repository will attempt to reestablish the database + * connection and try the operation again. + * + * @return whatever value is returned by the invoked operation. + */ + protected Object execute (Operation op) + throws PersistenceException + { + return execute(op, true); + } + + /** + * Executes the supplied operation followed by a call to + * commit() on the connection unless a + * PersistenceException or runtime error occurs, in which + * case a call to rollback() is executed on the + * connection. + * + * @param retryOnTransientFailure if true and the operation fails due + * to a transient failure (like losing the connection to the database + * or deadlock detection), the connection to the database will be + * reestablished (if necessary) and the operation attempted once more. + * + * @return whatever value is returned by the invoked operation. + */ + protected Object execute (Operation op, boolean retryOnTransientFailure) + throws PersistenceException + { + // obtain our database connection + Connection conn = null; + try { + conn = _provider.getConnection(_dbident); + conn.setAutoCommit(false); + gotConnection(conn); + } catch (SQLException sqe) { + String err = "Unable to obtain connection."; + throw new PersistenceException(err, sqe); + } + + // obtain a liaison for this connection + DatabaseLiaison liaison = LiaisonRegistry.getLiaison(conn); + Object rv = null; + + try { + // invoke the operation + rv = op.invoke(conn, liaison); + + // commit the transaction + if (liaison.supportsTransactions()) { + conn.commit(); + } + + // return the operation result + return rv; + + } catch (SQLException sqe) { + // back out our changes if something got hosed + if (liaison.supportsTransactions()) { + try { + conn.rollback(); + } catch (SQLException rbe) { + Log.warning("Unable to roll back operation."); + Log.logStackTrace(rbe); + } + } + + // let the connection provider know that the connection failed + _provider.connectionFailed(_dbident, conn, sqe); + // clear out the reference so that we don't release it later + conn = null; + + // if this is a transient failure and we've been requested to + // retry such failures, try one more time + if (retryOnTransientFailure && + liaison.isTransientException(sqe)) { + Log.info("Transient failure executing operation, " + + "retrying [error=" + sqe + "]."); + return execute(op, false); + } + + String err = "Operation invocation failed."; + throw new PersistenceException(err, sqe); + + } catch (PersistenceException pe) { + // back out our changes if something got hosed + if (liaison.supportsTransactions()) { + try { + conn.rollback(); + } catch (SQLException rbe) { + Log.warning("Unable to roll back operation."); + Log.logStackTrace(rbe); + } + } + throw pe; + + } catch (RuntimeException rte) { + // back out our changes if something got hosed + if (conn != null && liaison.supportsTransactions()) { + try { + conn.rollback(); + } catch (SQLException rbe) { + Log.warning("Unable to roll back operation."); + Log.logStackTrace(rbe); + } + } + throw rte; + + } finally { + if (conn != null) { + // release the database connection + _provider.releaseConnection(_dbident, conn); + } + } + } + + /** + * Called when we fetch a connection from the provider. This gives + * derived classes an opportunity to configure whatever internals they + * might be using with the connection that was fetched. + */ + protected void gotConnection (Connection conn) + { + } + + protected String _dbident; +} diff --git a/projects/samskivert/src/java/com/samskivert/jdbc/StaticConnectionProvider.java b/projects/samskivert/src/java/com/samskivert/jdbc/StaticConnectionProvider.java new file mode 100644 index 00000000..1967a06b --- /dev/null +++ b/projects/samskivert/src/java/com/samskivert/jdbc/StaticConnectionProvider.java @@ -0,0 +1,191 @@ +// +// $Id: StaticConnectionProvider.java,v 1.1 2001/09/20 01:53:20 mdb Exp $ +// +// samskivert library - useful routines for java programs +// Copyright (C) 2001 Michael Bayne +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation; either version 2.1 of the License, or +// (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.samskivert.jdbc; + +import java.io.IOException; +import java.sql.*; +import java.util.*; + +import com.samskivert.Log; +import com.samskivert.util.ConfigUtil; +import com.samskivert.util.PropertiesUtil; +import com.samskivert.util.StringUtil; + +/** + * The static connection provider generates JDBC connections based on + * configuration information provided via a properties file. It does no + * connection pooling and always returns the same connection for a + * particular identifier (unless that connection need be closed because of + * a connection failure, in which case it opens a new one the next time + * the connection is requested). + * + *

The configuration properties file should contain the following + * information: + * + *

+ * IDENT.driver=[jdbc driver class]
+ * IDENT.url=[jdbc driver url]
+ * IDENT.username=[jdbc username]
+ * IDENT.password=[jdbc password]
+ *
+ * [...]
+ * 
+ * + * Where IDENT is the database identifier for a particular + * database connection. When a particular database identifier is + * requested, the configuration information will be fetched from the + * properties. + */ +public class StaticConnectionProvider implements ConnectionProvider +{ + /** + * Constructs a static connection provider which will load its + * configuration from a properties file accessible via the classpath + * of the running application and identified by the specified path. + * + * @param propPath the path (relative to the classpath) to the + * properties file that will be used for configuration information. + * + * @exception IOException thrown if an error occurs locating or + * loading the specified properties file. + */ + public StaticConnectionProvider (String propPath) + throws IOException + { + this(ConfigUtil.loadProperties(propPath)); + } + + /** + * Constructs a static connection provider which will fetch its + * configuration information from the specified properties object. + * + * @param props the configuration for this connection provider. + */ + public StaticConnectionProvider (Properties props) + { + _props = props; + } + + /** + * Closes all of the open database connections in preparation for + * shutting down. + */ + public void shutdown () + { + // close all of the connections + Iterator iter = _conns.keySet().iterator(); + while (iter.hasNext()) { + String ident = (String)iter.next(); + Connection conn = (Connection)_conns.get(ident); + try { + conn.close(); + } catch (SQLException sqe) { + Log.warning("Error shutting down connection " + + "[ident=" + ident + ", err=" + sqe + "]."); + } + } + + // clear out the connection table + _conns.clear(); + } + + // documentation inherited + public Connection getConnection (String ident) + throws PersistenceException + { + Connection conn = (Connection)_conns.get(ident); + + // open the connection if we haven't already + if (conn == null) { + Properties props = + PropertiesUtil.getSubProperties(_props, ident); + + // get the JDBC configuration info + String err = "No driver class specified [ident=" + ident + "]."; + String driver = requireProp(props, "driver", err); + err = "No driver URL specified [ident=" + ident + "]."; + String url = requireProp(props, "url", err); + err = "No driver username specified [ident=" + ident + "]."; + String username = requireProp(props, "username", err); + err = "No driver password specified [ident=" + ident + "]."; + String password = requireProp(props, "password", err); + + // load up the driver class + try { + Class.forName(driver); + } catch (Exception e) { + err = "Error loading driver [ident=" + ident + + ", class=" + driver + "]."; + throw new PersistenceException(err, e); + } + + // create the connection + try { + conn = DriverManager.getConnection(url, username, password); + } catch (SQLException sqe) { + err = "Error creating database connection " + + "[ident=" + ident + "]."; + throw new PersistenceException(err, sqe); + } + + // cache the connection + _conns.put(ident, conn); + } + + return conn; + } + + // documentation inherited + public void releaseConnection (String ident, Connection conn) + { + // nothing to do here, all is well + } + + // documentation inherited + public void connectionFailed (String ident, Connection conn, + SQLException error) + { + // attempt to close the connection + try { + conn.close(); + } catch (SQLException sqe) { + Log.warning("Error closing failed connection [ident=" + ident + + ", error=" + sqe + "]."); + } + + // and remove it from the cache + _conns.remove(ident); + } + + protected static String requireProp (Properties props, + String name, String errmsg) + throws PersistenceException + { + String value = props.getProperty(name); + if (StringUtil.blank(value)) { + throw new PersistenceException(errmsg); + } + return value; + } + + protected Properties _props; + protected HashMap _conns = new HashMap(); +} diff --git a/projects/samskivert/src/java/com/samskivert/jdbc/jora/Session.java b/projects/samskivert/src/java/com/samskivert/jdbc/jora/Session.java index a18e4031..7fe6598d 100644 --- a/projects/samskivert/src/java/com/samskivert/jdbc/jora/Session.java +++ b/projects/samskivert/src/java/com/samskivert/jdbc/jora/Session.java @@ -13,6 +13,8 @@ package com.samskivert.jdbc.jora; import java.util.*; import java.sql.*; +import com.samskivert.Log; + /** * This class is reposnsible for establishing connection with database * and handling database errors. @@ -31,10 +33,47 @@ public class Session { connectionID = 0; } + /** Construct a session with a pre-existing connection instance. In + * this case, {@link #open} should not be called on this session. + * + * @param conn the connection to use. + */ + public Session(Connection connection) + { + connectionID = 0; + preparedStmtHash = new Hashtable(); + setConnection(connection); + } + /** Session consructor for ODBC bridge driver */ public Session() { this("sun.jdbc.odbc.JdbcOdbcDriver"); } + /** Sets the connection that should be used by this session. + */ + public void setConnection(Connection conn) + { + // only up the connection id if this is a new connection + if (connection != conn) { + // clear out our prepared statement hash because we've got a + // new connection + Enumeration items = preparedStmtHash.elements(); + while (items.hasMoreElements()) { + try { + ((PreparedStatement)items.nextElement()).close(); + } catch (SQLException sqe) { + Log.warning("Error closing cached prepared statement " + + "[error=" + sqe + "]."); + } + } + preparedStmtHash.clear(); + + // switch to our new connection + connection = conn; + connectionID += 1; + } + } + /** Handler of database session errors. Programmer should override * this method in derived class in order to provide application * dependent error handling. diff --git a/projects/samskivert/src/java/com/samskivert/servlet/user/UserExistsException.java b/projects/samskivert/src/java/com/samskivert/servlet/user/UserExistsException.java index c43e1e9c..b700af0b 100644 --- a/projects/samskivert/src/java/com/samskivert/servlet/user/UserExistsException.java +++ b/projects/samskivert/src/java/com/samskivert/servlet/user/UserExistsException.java @@ -1,5 +1,5 @@ // -// $Id: UserExistsException.java,v 1.3 2001/08/12 01:34:31 mdb Exp $ +// $Id: UserExistsException.java,v 1.4 2001/09/20 01:53:20 mdb Exp $ // // samskivert library - useful routines for java programs // Copyright (C) 2001 Michael Bayne @@ -20,11 +20,13 @@ package com.samskivert.servlet.user; +import com.samskivert.jdbc.PersistenceException; + /** * Thrown during user account creation when a user with the requested * username already exists. */ -public class UserExistsException extends Exception +public class UserExistsException extends PersistenceException { public UserExistsException (String message) { diff --git a/projects/samskivert/src/java/com/samskivert/servlet/user/UserManager.java b/projects/samskivert/src/java/com/samskivert/servlet/user/UserManager.java index 6ba6aeec..93082688 100644 --- a/projects/samskivert/src/java/com/samskivert/servlet/user/UserManager.java +++ b/projects/samskivert/src/java/com/samskivert/servlet/user/UserManager.java @@ -1,5 +1,5 @@ // -// $Id: UserManager.java,v 1.8 2001/08/11 22:43:28 mdb Exp $ +// $Id: UserManager.java,v 1.9 2001/09/20 01:53:20 mdb Exp $ // // samskivert library - useful routines for java programs // Copyright (C) 2001 Michael Bayne @@ -21,11 +21,12 @@ package com.samskivert.servlet.user; import java.net.URLEncoder; -import java.sql.SQLException; import java.util.Properties; import javax.servlet.http.*; import com.samskivert.Log; +import com.samskivert.jdbc.ConnectionProvider; +import com.samskivert.jdbc.PersistenceException; import com.samskivert.servlet.RedirectException; import com.samskivert.servlet.util.RequestUtils; import com.samskivert.util.*; @@ -39,8 +40,8 @@ public class UserManager { /** * A user manager creates a user repository through which to load and - * save user records. The properties needed to configure the user - * repository must be provided to the user manager at construct time. + * save user records. A connection provider must be supplied via which + * the user repository can obtain its database connection. * *

Presently the user manager requires the following configuration * information: @@ -59,16 +60,20 @@ public class UserManager * they are authenticated. * * + * @param config the user manager configuration properties. + * @param provider a database connection provider that will be used by + * the user repository to obtain it's database connection. + * * @see UserRepository#UserRepository */ - public UserManager (Properties props) - throws SQLException + public UserManager (Properties config, ConnectionProvider provider) + throws PersistenceException { // open up the user repository - _repository = new UserRepository(props); + _repository = new UserRepository(provider); // fetch the login URL from the properties - _loginURL = props.getProperty("login_url"); + _loginURL = config.getProperty("login_url"); if (_loginURL == null) { Log.warning("No login_url supplied in user manager config. " + "Authentication won't work."); @@ -80,8 +85,8 @@ public class UserManager { try { _repository.pruneSessions(); - } catch (SQLException sqe) { - Log.warning("Error pruning session table: " + sqe); + } catch (PersistenceException pe) { + Log.warning("Error pruning session table: " + pe); } } }; @@ -91,13 +96,6 @@ public class UserManager public void shutdown () { - // shut down the user repository - try { - _repository.shutdown(); - } catch (SQLException sqe) { - Log.warning("Error shutting down user repository: " + sqe); - } - // cancel our session table pruning thread IntervalManager.remove(_prunerid); } @@ -119,7 +117,7 @@ public class UserManager * bogus. */ public User loadUser (HttpServletRequest req) - throws SQLException + throws PersistenceException { String authcode = getAuthCode(req); if (authcode != null) { @@ -139,7 +137,7 @@ public class UserManager * @return the user associated with the request. */ public User requireUser (HttpServletRequest req) - throws SQLException, RedirectException + throws PersistenceException, RedirectException { User user = loadUser(req); // if no user was loaded, we need to redirect these fine people to @@ -172,7 +170,8 @@ public class UserManager */ public User login (String username, String password, boolean persist, HttpServletResponse rsp) - throws SQLException, NoSuchUserException, InvalidPasswordException + throws PersistenceException, NoSuchUserException, + InvalidPasswordException { // load up the requested user User user = _repository.loadUser(username); diff --git a/projects/samskivert/src/java/com/samskivert/servlet/user/UserRepository.java b/projects/samskivert/src/java/com/samskivert/servlet/user/UserRepository.java index b809808f..64cfa913 100644 --- a/projects/samskivert/src/java/com/samskivert/servlet/user/UserRepository.java +++ b/projects/samskivert/src/java/com/samskivert/servlet/user/UserRepository.java @@ -1,5 +1,5 @@ // -// $Id: UserRepository.java,v 1.16 2001/09/15 17:22:11 mdb Exp $ +// $Id: UserRepository.java,v 1.17 2001/09/20 01:53:20 mdb Exp $ // // samskivert library - useful routines for java programs // Copyright (C) 2001 Michael Bayne @@ -28,8 +28,7 @@ import java.util.Properties; import org.apache.regexp.*; import com.samskivert.Log; -import com.samskivert.jdbc.JDBCUtil; -import com.samskivert.jdbc.MySQLRepository; +import com.samskivert.jdbc.*; import com.samskivert.jdbc.jora.*; import com.samskivert.util.HashIntMap; @@ -38,33 +37,32 @@ import com.samskivert.util.HashIntMap; * user repository encapsulates the creating, loading and management of * users and sessions. */ -public class UserRepository extends MySQLRepository +public class UserRepository extends JORARepository { /** - * Creates the repository and opens the user database. A properties - * object should be supplied with the following fields: - * - *

-     * driver=[jdbc driver class]
-     * url=[jdbc driver url]
-     * username=[jdbc username]
-     * password=[jdbc password]
-     * 
- * - * @param props a properties object containing the configuration - * parameters for the repository. + * The database identifier used to obtain a connection from our + * connection provider. The value is userdb which you'll + * probably need to know to provide the proper configuration to your + * connection provider. */ - public UserRepository (Properties props) - throws SQLException + public static final String USER_REPOSITORY_IDENT = "userdb"; + + /** + * Creates the repository and opens the user database. The database + * identifier used to fetch our database connection is documented by + * {@link #USER_REPOSITORY_IDENT}. + * + * @param provider the database connection provider. + */ + public UserRepository (ConnectionProvider provider) { - super(props); + super(provider, USER_REPOSITORY_IDENT); } - protected void createTables () - throws SQLException + protected void createTables (Session session) { // create our table object - _utable = new Table(User.class.getName(), "users", _session, + _utable = new Table(User.class.getName(), "users", session, "userid"); } @@ -83,7 +81,7 @@ public class UserRepository extends MySQLRepository */ public int createUser (String username, String password, String realname, String email) - throws InvalidUsernameException, UserExistsException, SQLException + throws InvalidUsernameException, UserExistsException, PersistenceException { // check minimum length if (username.length() < MINIMUM_USERNAME_LENGTH) { @@ -103,25 +101,26 @@ public class UserRepository extends MySQLRepository user.email = email; user.created = new Date(System.currentTimeMillis()); - try { - execute(new Operation () { - public Object invoke () throws SQLException - { + execute(new Operation () { + public Object invoke (Connection conn, DatabaseLiaison liaison) + throws PersistenceException, SQLException + { + try { _utable.insert(user); // update the userid now that it's known - user.userid = lastInsertedId(); + user.userid = liaison.lastInsertedId(conn); // nothing to return return null; - } - }); - } catch (SQLException sqe) { - if (isDuplicateRowException(sqe)) { - throw new UserExistsException("error.user_exists"); - } else { - throw sqe; - } - } + } catch (SQLException sqe) { + if (liaison.isDuplicateRowException(sqe)) { + throw new UserExistsException("error.user_exists"); + } else { + throw sqe; + } + } + } + }); return user.userid; } @@ -133,10 +132,11 @@ public class UserRepository extends MySQLRepository * that id exists. */ public User loadUser (final String username) - throws SQLException + throws PersistenceException { return (User)execute(new Operation () { - public Object invoke () throws SQLException + public Object invoke (Connection conn, DatabaseLiaison liaison) + throws PersistenceException, SQLException { // look up the user String query = "where username = '" + username + "'"; @@ -161,10 +161,11 @@ public class UserRepository extends MySQLRepository * that id exists. */ public User loadUser (final int userid) - throws SQLException + throws PersistenceException { return (User)execute(new Operation () { - public Object invoke () throws SQLException + public Object invoke (Connection conn, DatabaseLiaison liaison) + throws PersistenceException, SQLException { // look up the user Cursor ec = _utable.select("where userid = " + userid); @@ -188,10 +189,11 @@ public class UserRepository extends MySQLRepository * no session exists with the supplied identifier. */ public User loadUserBySession (final String sessionKey) - throws SQLException + throws PersistenceException { return (User)execute(new Operation () { - public Object invoke () throws SQLException + public Object invoke (Connection conn, DatabaseLiaison liaison) + throws PersistenceException, SQLException { String query = "where authcode = '" + sessionKey + "' AND sessions.userid = users.userid"; @@ -214,10 +216,11 @@ public class UserRepository extends MySQLRepository * Updates a user that was previously fetched from the repository. */ public void updateUser (final User user) - throws SQLException + throws PersistenceException { execute(new Operation () { - public Object invoke () throws SQLException + public Object invoke (Connection conn, DatabaseLiaison liaison) + throws PersistenceException, SQLException { _utable.update(user); // nothing to return @@ -230,10 +233,11 @@ public class UserRepository extends MySQLRepository * Removes the user from the repository. */ public void deleteUser (final User user) - throws SQLException + throws PersistenceException { execute(new Operation () { - public Object invoke () throws SQLException + public Object invoke (Connection conn, DatabaseLiaison liaison) + throws PersistenceException, SQLException { _utable.delete(user); // nothing to return @@ -251,7 +255,7 @@ public class UserRepository extends MySQLRepository * sessions expire after one month. */ public String createNewSession (final User user, boolean persist) - throws SQLException + throws PersistenceException { // generate a random session identifier final String authcode = UserUtil.genAuthCode(user); @@ -263,7 +267,8 @@ public class UserRepository extends MySQLRepository // insert the session into the database execute(new Operation () { - public Object invoke () throws SQLException + public Object invoke (Connection conn, DatabaseLiaison liaison) + throws PersistenceException, SQLException { _session.execute("insert into sessions " + "(authcode, userid, expires) values('" + @@ -282,10 +287,11 @@ public class UserRepository extends MySQLRepository * Prunes any expired sessions from the sessions table. */ public void pruneSessions () - throws SQLException + throws PersistenceException { execute(new Operation () { - public Object invoke () throws SQLException + public Object invoke (Connection conn, DatabaseLiaison liaison) + throws PersistenceException, SQLException { _session.execute("delete from sessions where " + "expires <= CURRENT_DATE()"); @@ -301,7 +307,7 @@ public class UserRepository extends MySQLRepository * slot in the array will contain a null. */ public String[] loadUserNames (int[] userids) - throws SQLException + throws PersistenceException { return loadNames(userids, "username"); } @@ -312,13 +318,13 @@ public class UserRepository extends MySQLRepository * slot in the array will contain a null. */ public String[] loadRealNames (int[] userids) - throws SQLException + throws PersistenceException { return loadNames(userids, "realname"); } protected String[] loadNames (int[] userids, final String column) - throws SQLException + throws PersistenceException { // if userids is zero length, we've got no work to do if (userids.length == 0) { @@ -338,7 +344,8 @@ public class UserRepository extends MySQLRepository // do the query execute(new Operation () { - public Object invoke () throws SQLException + public Object invoke (Connection conn, DatabaseLiaison liaison) + throws PersistenceException, SQLException { Statement stmt = _session.connection.createStatement(); try { @@ -377,13 +384,14 @@ public class UserRepository extends MySQLRepository * already using it to link up. */ public String[] loadAllRealNames () - throws SQLException + throws PersistenceException { final ArrayList names = new ArrayList(); // do the query execute(new Operation () { - public Object invoke () throws SQLException + public Object invoke (Connection conn, DatabaseLiaison liaison) + throws PersistenceException, SQLException { Statement stmt = _session.connection.createStatement(); try { @@ -411,13 +419,17 @@ public class UserRepository extends MySQLRepository public static void main (String[] args) { Properties props = new Properties(); - props.put("driver", "org.gjt.mm.mysql.Driver"); - props.put("url", "jdbc:mysql://localhost:3306/samskivert"); - props.put("username", "www"); - props.put("password", "Il0ve2PL@Y"); + props.put(USER_REPOSITORY_IDENT + ".driver", + "org.gjt.mm.mysql.Driver"); + props.put(USER_REPOSITORY_IDENT + ".url", + "jdbc:mysql://localhost:3306/samskivert"); + props.put(USER_REPOSITORY_IDENT + ".username", "www"); + props.put(USER_REPOSITORY_IDENT + ".password", "Il0ve2PL@Y"); try { - UserRepository rep = new UserRepository(props); + StaticConnectionProvider scp = + new StaticConnectionProvider(props); + UserRepository rep = new UserRepository(scp); System.out.println(rep.loadUser("mdb")); System.out.println(rep.loadUserBySession("auth")); @@ -427,7 +439,7 @@ public class UserRepository extends MySQLRepository rep.createUser("mdb", "foobar", "Michael Bayne", "mdb@samskivert.com"); - rep.shutdown(); + scp.shutdown(); } catch (Throwable t) { t.printStackTrace(System.err);