Serious database services rethink. It's cleaner, more loosely coupled and
more extensible now. Whee! git-svn-id: https://samskivert.googlecode.com/svn/trunk@323 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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. <code>close()</code> <em>will not</em> 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 <code>getConnection</code> 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);
|
||||
}
|
||||
@@ -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 <code>AUTO_INCREMENT</code> 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
|
||||
* <code>AUTO_INCREMENT</code> table or -1 if it could not be
|
||||
* obtained.
|
||||
*/
|
||||
public int lastInsertedId (Connection conn) throws SQLException;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*
|
||||
* <p> These services are based on the JORA Java/RDBMS interoperability
|
||||
* <p> 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:
|
||||
*
|
||||
* <pre>
|
||||
* public class PeopleRepository extends SimpleRepository
|
||||
* {
|
||||
* public Person getPerson (int personid);
|
||||
* public Person[] getPeopleByFirstName (String firstName);
|
||||
* public void updatePerson (Person person);
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p> It is probably also desirable to catch SQL exceptions and wrap them
|
||||
* in the <code>PersistenceException</code> class that is provided by this
|
||||
* package.
|
||||
*
|
||||
* <p> The repository comes in a few flavors depending on the needs of the
|
||||
* persistence services being developed:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link SimpleRepository}: The simple repository is used by services
|
||||
* that need access to a single JDBC connection to perform their database
|
||||
* operations.</li>
|
||||
*
|
||||
* <li>{@link JORARepository}: JORA repository is used by services that
|
||||
* wish to make use of the JORA Java/RDBMS interoperability package.</li>
|
||||
*
|
||||
* <li> Because the repository provides a unified interface to a
|
||||
* particular persistence service, it is conceivable that it would need to
|
||||
* talk to multiple databases to provide those services. Presently, the
|
||||
* repository only supports a single connection, but if the need arose,
|
||||
* implementing a repository flavor that supported multiple connections
|
||||
* would be the proper solution.</li>
|
||||
* </ul>
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* <pre>
|
||||
* driver=[jdbc driver class]
|
||||
* url=[jdbc driver url]
|
||||
* username=[jdbc username]
|
||||
* password=[jdbc password]
|
||||
* </pre>
|
||||
*
|
||||
* <p> The connection to the database will not be established until
|
||||
* the first operation is executed. It is important that database
|
||||
* operations be invoked via <code>execute()</code> 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 <code>ensureConnection()</code> 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 <code>execute</code>.
|
||||
*
|
||||
* @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
|
||||
* <em>will</em> 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.
|
||||
*
|
||||
* <p> 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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
* <code>commit()</code> on the connection unless a
|
||||
* <code>PersistenceException</code> or runtime error occurs, in which
|
||||
* case a call to <code>rollback()</code> 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;
|
||||
}
|
||||
@@ -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).
|
||||
*
|
||||
* <p> The configuration properties file should contain the following
|
||||
* information:
|
||||
*
|
||||
* <pre>
|
||||
* IDENT.driver=[jdbc driver class]
|
||||
* IDENT.url=[jdbc driver url]
|
||||
* IDENT.username=[jdbc username]
|
||||
* IDENT.password=[jdbc password]
|
||||
*
|
||||
* [...]
|
||||
* </pre>
|
||||
*
|
||||
* Where <code>IDENT</code> 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();
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
* <p> Presently the user manager requires the following configuration
|
||||
* information:
|
||||
@@ -59,16 +60,20 @@ public class UserManager
|
||||
* they are authenticated.
|
||||
* </ul>
|
||||
*
|
||||
* @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);
|
||||
|
||||
@@ -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:
|
||||
*
|
||||
* <pre>
|
||||
* driver=[jdbc driver class]
|
||||
* url=[jdbc driver url]
|
||||
* username=[jdbc username]
|
||||
* password=[jdbc password]
|
||||
* </pre>
|
||||
*
|
||||
* @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 <code>userdb</code> 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);
|
||||
|
||||
Reference in New Issue
Block a user