AbstractJdbcUsersRepositorypublic abstract class AbstractJdbcUsersRepository extends AbstractUsersRepository implements org.apache.avalon.framework.service.Serviceable, org.apache.avalon.framework.activity.Initializable, org.apache.avalon.framework.context.Contextualizable, org.apache.avalon.framework.configuration.ConfigurableAn abstract base class for creating UserRepository implementations
which use a database for persistence.
To implement a new UserRepository using by extending this class,
you need to implement the 3 abstract methods defined below,
and define the required SQL statements in an SQLResources
file.
The SQL statements used by this implementation are:
| Required |
select | Select all users. |
insert | Insert a user. |
update | Update a user. |
delete | Delete a user by name. |
createTable | Create the users table. |
| Optional |
selectByLowercaseName | Select a user by name (case-insensitive lowercase). |
|
Fields Summary |
---|
protected org.apache.avalon.framework.context.Context | contextThe Avalon context used by the instance | protected Map | m_sqlParameters | private String | m_sqlFileName | private String | m_datasourceName | private org.apache.avalon.cornerstone.services.datasources.DataSourceSelector | m_datasources | private org.apache.avalon.excalibur.datasource.DataSourceComponent | m_datasource | private String | m_getUsersSql | private String | m_userByNameCaseInsensitiveSql | private String | m_insertUserSql | private String | m_updateUserSql | private String | m_deleteUserSql | private String | m_createUserTableSql | private org.apache.james.util.JDBCUtil | theJDBCUtil |
Methods Summary |
---|
public void | configure(org.apache.avalon.framework.configuration.Configuration configuration)Configures the UserRepository for JDBC access.
Requires a configuration element in the .conf.xml file of the form:
<repository name="LocalUsers"
class="org.apache.james.userrepository.JamesUsersJdbcRepository">
<!-- Name of the datasource to use -->
<data-source>MailDb</data-source>
<!-- File to load the SQL definitions from -->
<sqlFile>dist/conf/sqlResources.xml</sqlFile>
<!-- replacement parameters for the sql file -->
<sqlParameters table="JamesUsers"/>
</repository>
StringBuffer logBuffer = null;
if (getLogger().isDebugEnabled()) {
logBuffer =
new StringBuffer(64)
.append(this.getClass().getName())
.append(".configure()");
getLogger().debug( logBuffer.toString() );
}
// Parse the DestinationURL for the name of the datasource,
// the table to use, and the (optional) repository Key.
String destUrl = configuration.getAttribute("destinationURL");
// normalise the destination, to simplify processing.
if ( ! destUrl.endsWith("/") ) {
destUrl += "/";
}
// Split on "/", starting after "db://"
List urlParams = new ArrayList();
int start = 5;
int end = destUrl.indexOf('/", start);
while ( end > -1 ) {
urlParams.add(destUrl.substring(start, end));
start = end + 1;
end = destUrl.indexOf('/", start);
}
// Build SqlParameters and get datasource name from URL parameters
m_sqlParameters = new HashMap();
switch ( urlParams.size() ) {
case 3:
m_sqlParameters.put("key", urlParams.get(2));
case 2:
m_sqlParameters.put("table", urlParams.get(1));
case 1:
m_datasourceName = (String)urlParams.get(0);
break;
default:
throw new ConfigurationException
("Malformed destinationURL - " +
"Must be of the format \"db://<data-source>[/<table>[/<key>]]\".");
}
if (getLogger().isDebugEnabled()) {
logBuffer =
new StringBuffer(128)
.append("Parsed URL: table = '")
.append(m_sqlParameters.get("table"))
.append("', key = '")
.append(m_sqlParameters.get("key"))
.append("'");
getLogger().debug(logBuffer.toString());
}
// Get the SQL file location
m_sqlFileName = configuration.getChild("sqlFile", true).getValue();
if (!m_sqlFileName.startsWith("file://")) {
throw new ConfigurationException
("Malformed sqlFile - Must be of the format \"file://<filename>\".");
}
// Get other sql parameters from the configuration object,
// if any.
Configuration sqlParamsConfig = configuration.getChild("sqlParameters");
String[] paramNames = sqlParamsConfig.getAttributeNames();
for (int i = 0; i < paramNames.length; i++ ) {
String paramName = paramNames[i];
String paramValue = sqlParamsConfig.getAttribute(paramName);
m_sqlParameters.put(paramName, paramValue);
}
| public void | contextualize(org.apache.avalon.framework.context.Context context)
this.context = context;
| protected void | doAddUser(org.apache.james.services.User user)Adds a user to the underlying Repository.
The user name must not clash with an existing user.
Connection conn = openConnection();
PreparedStatement addUserStatement = null;
// Insert into the database.
try {
// Get a PreparedStatement for the insert.
addUserStatement =
conn.prepareStatement(m_insertUserSql);
setUserForInsertStatement(user, addUserStatement);
addUserStatement.execute();
}
catch ( SQLException sqlExc) {
sqlExc.printStackTrace();
throw new CascadingRuntimeException("Error accessing database", sqlExc);
} finally {
theJDBCUtil.closeJDBCStatement(addUserStatement);
theJDBCUtil.closeJDBCConnection(conn);
}
| protected void | doRemoveUser(org.apache.james.services.User user)Removes a user from the underlying repository.
If the user doesn't exist this method doesn't throw
an exception.
String username = user.getUserName();
Connection conn = openConnection();
PreparedStatement removeUserStatement = null;
// Delete from the database.
try {
removeUserStatement = conn.prepareStatement(m_deleteUserSql);
removeUserStatement.setString(1, username);
removeUserStatement.execute();
}
catch ( SQLException sqlExc ) {
sqlExc.printStackTrace();
throw new CascadingRuntimeException("Error accessing database", sqlExc);
} finally {
theJDBCUtil.closeJDBCStatement(removeUserStatement);
theJDBCUtil.closeJDBCConnection(conn);
}
| protected void | doUpdateUser(org.apache.james.services.User user)Updates a user record to match the supplied User.
Connection conn = openConnection();
PreparedStatement updateUserStatement = null;
// Update the database.
try {
updateUserStatement = conn.prepareStatement(m_updateUserSql);
setUserForUpdateStatement(user, updateUserStatement);
updateUserStatement.execute();
}
catch ( SQLException sqlExc ) {
sqlExc.printStackTrace();
throw new CascadingRuntimeException("Error accessing database", sqlExc);
} finally {
theJDBCUtil.closeJDBCStatement(updateUserStatement);
theJDBCUtil.closeJDBCConnection(conn);
}
| private java.util.Collection | getAllUsers()Returns a list populated with all of the Users in the repository.
List userList = new ArrayList(); // Build the users into this list.
Connection conn = openConnection();
PreparedStatement getUsersStatement = null;
ResultSet rsUsers = null;
try {
// Get a ResultSet containing all users.
getUsersStatement =
conn.prepareStatement(m_getUsersSql);
rsUsers = getUsersStatement.executeQuery();
// Loop through and build a User for every row.
while ( rsUsers.next() ) {
User user = readUserFromResultSet(rsUsers);
userList.add(user);
}
}
catch ( SQLException sqlExc) {
sqlExc.printStackTrace();
throw new CascadingRuntimeException("Error accessing database", sqlExc);
}
finally {
theJDBCUtil.closeJDBCResultSet(rsUsers);
theJDBCUtil.closeJDBCStatement(getUsersStatement);
theJDBCUtil.closeJDBCConnection(conn);
}
return userList;
| protected org.apache.james.services.User | getUserByName(java.lang.String name, boolean ignoreCase)Gets a user by name, ignoring case if specified.
If the specified SQL statement has been defined, this method
overrides the basic implementation in AbstractUsersRepository
to increase performance.
// See if this statement has been set, if not, use
// simple superclass method.
if ( m_userByNameCaseInsensitiveSql == null ) {
return super.getUserByName(name, ignoreCase);
}
// Always get the user via case-insensitive SQL,
// then check case if necessary.
Connection conn = openConnection();
PreparedStatement getUsersStatement = null;
ResultSet rsUsers = null;
try {
// Get a ResultSet containing all users.
String sql = m_userByNameCaseInsensitiveSql;
getUsersStatement = conn.prepareStatement(sql);
getUsersStatement.setString(1, name.toLowerCase(Locale.US));
rsUsers = getUsersStatement.executeQuery();
// For case-insensitive matching, the first matching user will be returned.
User user = null;
while ( rsUsers.next() ) {
User rowUser = readUserFromResultSet(rsUsers);
String actualName = rowUser.getUserName();
// Check case before we assume it's the right one.
if ( ignoreCase || actualName.equals(name) ) {
user = rowUser;
break;
}
}
return user;
}
catch ( SQLException sqlExc ) {
sqlExc.printStackTrace();
throw new CascadingRuntimeException("Error accessing database", sqlExc);
}
finally {
theJDBCUtil.closeJDBCResultSet(rsUsers);
theJDBCUtil.closeJDBCStatement(getUsersStatement);
theJDBCUtil.closeJDBCConnection(conn);
}
| public void | initialize()Initialises the JDBC repository.
1) Tests the connection to the database.
2) Loads SQL strings from the SQL definition file,
choosing the appropriate SQL for this connection,
and performing parameter substitution,
3) Initialises the database with the required tables, if necessary.
StringBuffer logBuffer = null;
if (getLogger().isDebugEnabled()) {
logBuffer =
new StringBuffer(128)
.append(this.getClass().getName())
.append(".initialize()");
getLogger().debug( logBuffer.toString() );
}
theJDBCUtil =
new JDBCUtil() {
protected void delegatedLog(String logString) {
AbstractJdbcUsersRepository.this.getLogger().warn("AbstractJdbcUsersRepository: " + logString);
}
};
// Get the data-source required.
m_datasource = (DataSourceComponent)m_datasources.select(m_datasourceName);
// Test the connection to the database, by getting the DatabaseMetaData.
Connection conn = openConnection();
try{
DatabaseMetaData dbMetaData = conn.getMetaData();
File sqlFile = null;
try {
sqlFile = AvalonContextUtilities.getFile(context, m_sqlFileName);
} catch (Exception e) {
getLogger().fatalError(e.getMessage(), e);
throw e;
}
if (getLogger().isDebugEnabled()) {
logBuffer =
new StringBuffer(256)
.append("Reading SQL resources from file: ")
.append(sqlFile.getAbsolutePath())
.append(", section ")
.append(this.getClass().getName())
.append(".");
getLogger().debug(logBuffer.toString());
}
SqlResources sqlStatements = new SqlResources();
sqlStatements.init(sqlFile, this.getClass().getName(),
conn, m_sqlParameters);
// Create the SQL Strings to use for this table.
// Fetches all Users from the db.
m_getUsersSql = sqlStatements.getSqlString("select", true);
// Get a user by lowercase name. (optional)
// If not provided, the entire list is iterated to find a user.
m_userByNameCaseInsensitiveSql =
sqlStatements.getSqlString("selectByLowercaseName");
// Insert, update and delete are not guaranteed to be case-insensitive
// Will always be called with correct case in username..
m_insertUserSql = sqlStatements.getSqlString("insert", true);
m_updateUserSql = sqlStatements.getSqlString("update", true);
m_deleteUserSql = sqlStatements.getSqlString("delete", true);
// Creates a single table with "username" the Primary Key.
m_createUserTableSql = sqlStatements.getSqlString("createTable", true);
// Check if the required table exists. If not, create it.
// The table name is defined in the SqlResources.
String tableName = sqlStatements.getSqlString("tableName", true);
// Need to ask in the case that identifiers are stored, ask the DatabaseMetaInfo.
// NB this should work, but some drivers (eg mm MySQL)
// don't return the right details, hence the hackery below.
/*
String tableName = m_tableName;
if ( dbMetaData.storesLowerCaseIdentifiers() ) {
tableName = tableName.toLowerCase(Locale.US);
}
else if ( dbMetaData.storesUpperCaseIdentifiers() ) {
tableName = tableName.toUpperCase(Locale.US);
}
*/
// Try UPPER, lower, and MixedCase, to see if the table is there.
if (! theJDBCUtil.tableExists(dbMetaData, tableName))
{
// Users table doesn't exist - create it.
PreparedStatement createStatement = null;
try {
createStatement =
conn.prepareStatement(m_createUserTableSql);
createStatement.execute();
} finally {
theJDBCUtil.closeJDBCStatement(createStatement);
}
logBuffer =
new StringBuffer(128)
.append(this.getClass().getName())
.append(": Created table \'")
.append(tableName)
.append("\'.");
getLogger().info(logBuffer.toString());
} else {
if (getLogger().isDebugEnabled()) {
getLogger().debug("Using table: " + tableName);
}
}
}
finally {
theJDBCUtil.closeJDBCConnection( conn );
}
| protected java.util.Iterator | listAllUsers()Returns a list populated with all of the Users in the repository.
return getAllUsers().iterator();
| protected java.util.List | listUserNames()Produces the complete list of User names, with correct case.
Collection users = getAllUsers();
List userNames = new ArrayList(users.size());
for (Iterator it = users.iterator(); it.hasNext(); ) {
userNames.add(((User)it.next()).getUserName());
}
users.clear();
return userNames;
| private java.sql.Connection | openConnection()Opens a connection, throwing a runtime exception if a SQLException is
encountered in the process.
try {
return m_datasource.getConnection();
}
catch (SQLException sqle) {
throw new CascadingRuntimeException(
"An exception occurred getting a database connection.", sqle);
}
| protected abstract org.apache.james.services.User | readUserFromResultSet(java.sql.ResultSet rsUsers)Reads properties for a User from an open ResultSet.
Subclass implementations of this method must have knowledge of the fields
presented by the "select" and "selectByLowercaseName" SQL statements.
These implemenations may generate a subclass-specific User instance.
| public void | service(org.apache.avalon.framework.service.ServiceManager componentManager)
StringBuffer logBuffer = null;
if (getLogger().isDebugEnabled())
{
logBuffer =
new StringBuffer(64)
.append(this.getClass().getName())
.append(".compose()");
getLogger().debug( logBuffer.toString() );
}
m_datasources =
(DataSourceSelector)componentManager.lookup( DataSourceSelector.ROLE );
| protected abstract void | setUserForInsertStatement(org.apache.james.services.User user, java.sql.PreparedStatement userInsert)Set parameters of a PreparedStatement object with
property values from a User instance.
Implementations of this method have knowledge of the parameter
ordering of the "insert" SQL statement definition.
| protected abstract void | setUserForUpdateStatement(org.apache.james.services.User user, java.sql.PreparedStatement userUpdate)Set parameters of a PreparedStatement object with
property values from a User instance.
Implementations of this method have knowledge of the parameter
ordering of the "update" SQL statement definition.
|
|