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: + * + *
- * 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);