/*
* This file is part of the WfMOpen project.
* Copyright (C) 2001-2005 Danet GmbH (www.danet.de), BU BTS.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Id: DatabaseRmsService.java 2095 2006-12-20 10:30:08Z drmlipp $
*
* $Log$
* Revision 1.9 2006/12/12 10:03:10 drmlipp
* Fixed authorizers.
*
* Revision 1.8 2006/10/15 19:29:51 mlipp
* Merged changes from 1.4.x up to 1.4ea3pre1.
*
* Revision 1.7.2.1 2006/10/14 21:34:05 mlipp
* Simplified resource assignment service implementation.
*
* Revision 1.7 2006/10/03 11:13:05 mlipp
* Improved.
*
* Revision 1.6 2006/10/02 20:54:32 mlipp
* Adapted to new resource base classes.
*
* Revision 1.5 2006/09/29 14:01:17 drmlipp
* Improved property names.
*
* Revision 1.4 2006/09/29 12:32:11 drmlipp
* Consistently using WfMOpen as projct name now.
*
* Revision 1.3 2006/09/29 11:39:16 drmlipp
* Various fixes.
*
* Revision 1.2 2006/09/28 21:28:09 mlipp
* Implementation continued.
*
* Revision 1.1 2006/09/28 15:03:56 drmlipp
* Getting started with db RMS.
*
*/
package de.danet.an.workflow.rmsimpls.dbrms;
import java.rmi.RemoteException;
import java.security.Principal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import javax.naming.NamingException;
import javax.sql.DataSource;
import de.danet.an.util.EJBUtil;
import de.danet.an.util.JDBCUtil;
import de.danet.an.workflow.omgcore.WfResource;
import de.danet.an.workflow.spis.rms.DefaultGroupResource;
import de.danet.an.workflow.spis.rms.DefaultRoleResource;
import de.danet.an.workflow.spis.rms.DefaultUserResource;
import de.danet.an.workflow.spis.rms.FactoryConfigurationError;
import de.danet.an.workflow.spis.rms.ResourceAssignmentContext;
import de.danet.an.workflow.spis.rms.ResourceManagementService;
import de.danet.an.workflow.spis.rms.ResourceNotFoundException;
/**
* Implements the {@link
* de.danet.an.workflow.spis.rms.ResourceManagementService resource
* management service} based on an database.
*
* @author <a href="mailto:lipp@danet.de">Michael Lipp</a>
* @version $Revision: 2095 $
*/
public class DatabaseRmsService implements ResourceManagementService {
private static final org.apache.commons.logging.Log logger
= org.apache.commons.logging.LogFactory.getLog(DatabaseRmsService.class);
private ResourceAssignmentContext rasCtx = null;
private Properties props = null;
/**
* Constructs a new resource management service.
*
* @param props the configuration properties
* @param rasvc the resource assignment service
* @throws FactoryConfigurationError if the required resources
* cannot be obtained
*/
public DatabaseRmsService
(Properties props, ResourceAssignmentContext rasCtx)
throws FactoryConfigurationError {
this.props = props;
this.rasCtx = rasCtx;
}
private DataSource ds() {
try {
return (DataSource)
EJBUtil.lookupJNDIEntry(props.getProperty("dataSource"));
} catch (NamingException e) {
throw (IllegalStateException)
(new IllegalStateException
("Configured data source "+props.getProperty("dataSource")
+ " is unknown: " + e.getMessage())).initCause(e);
}
}
/**
* Given a {@link java.security.Principal principal}, return the
* workflow resource associated with this principal by the
* resource management facility.<P>
*
* @param principal the principal.
* @return a <code>WfResource</code> object corresponding to the
* given principal.
* @throws ResourceNotFoundException if the StaffMember with the given key
* can't be found or the key is not associate with an StaffMember object.
* @throws RemoteException if a system-level error occurs.
*/
public WfResource asResource (Principal principal)
throws ResourceNotFoundException, RemoteException {
Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
con = ds().getConnection();
ps = con.prepareStatement(props.getProperty("userLookupQuery"));
ps.setString(1, principal.getName());
rs = ps.executeQuery();
if (!rs.next()) {
throw new ResourceNotFoundException
("No such user: " + principal.getName());
}
return new DefaultUserResource
(rasCtx, rs.getString(1), rs.getString(2));
} catch (SQLException e) {
throw new RemoteException (e.getMessage(), e);
} finally {
try {
JDBCUtil.closeAll(rs, ps, con);
} catch (SQLException e) {
logger.warn("Cannot close (ignored): " + e.getMessage(), e);
}
}
}
/* Comment copied from interface */
public Collection authorizers (WfResource wfResource)
throws RemoteException {
List result = new ArrayList ();
if (!(wfResource instanceof DefaultUserResource)) {
return result;
}
String userId = ((DefaultUserResource)wfResource).getId();
Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
con = ds().getConnection();
ps = con.prepareStatement(props.getProperty("rolesQuery"));
ps.setString(1, userId);
rs = ps.executeQuery();
while (rs.next()) {
result.add(new DefaultRoleResource
(rasCtx, rs.getString(1), rs.getString(2)));
}
rs.close();
rs = null;
ps.close();
ps = null;
ps = con.prepareStatement(props.getProperty("groupsQuery"));
ps.setString(1, userId);
rs = ps.executeQuery();
while (rs.next()) {
result.add(new DefaultGroupResource
(rasCtx, rs.getString(1), rs.getString(2)));
}
} catch (SQLException e) {
throw new RemoteException (e.getMessage(), e);
} finally {
try {
JDBCUtil.closeAll(rs, ps, con);
} catch (SQLException e) {
logger.warn("Cannot close (ignored): " + e.getMessage(), e);
}
}
return result;
}
/* Comment copied from interface */
public WfResource resourceByKey (String key)
throws ResourceNotFoundException, RemoteException {
Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
con = ds().getConnection();
if (DefaultUserResource.isValidKey(key)) {
String resId = DefaultUserResource.getId(key);
ps = con.prepareStatement(props.getProperty("userNameQuery"));
ps.setString(1, resId);
rs = ps.executeQuery();
if (!rs.next()) {
throw new ResourceNotFoundException
("No user with id: " + resId);
}
return new DefaultUserResource (rasCtx, resId, rs.getString(1));
} else if (DefaultGroupResource.isValidKey(key)) {
String resId = DefaultGroupResource.getId(key);
ps = con.prepareStatement(props.getProperty("groupNameQuery"));
ps.setString(1, resId);
rs = ps.executeQuery();
if (!rs.next()) {
throw new ResourceNotFoundException
("No group with id: " + resId);
}
return new DefaultGroupResource (rasCtx, resId, rs.getString(1));
} else if (DefaultRoleResource.isValidKey(key)) {
String resId = DefaultRoleResource.getId(key);
ps = con.prepareStatement(props.getProperty("roleNameQuery"));
ps.setString(1, resId);
rs = ps.executeQuery();
if (!rs.next()) {
throw new ResourceNotFoundException
("No role with id: " + resId);
}
return new DefaultRoleResource (rasCtx, resId, rs.getString(1));
} else {
throw new IllegalArgumentException
("Resource with invalid key " + key);
}
} catch (SQLException e) {
throw new RemoteException (e.getMessage(), e);
} finally {
try {
JDBCUtil.closeAll(rs, ps, con);
} catch (SQLException e) {
logger.warn("Cannot close (ignored): " + e.getMessage(), e);
}
}
}
/* Comment copied from interface. */
public Collection listResources () throws RemoteException {
Connection con = null;
try {
List result = new ArrayList ();
con = ds().getConnection();
listResourceType (result, con, "allUsersQuery");
listResourceType (result, con, "allGroupsQuery");
listResourceType (result, con, "allRolesQuery");
return result;
} catch (SQLException e) {
throw new RemoteException (e.getMessage(), e);
} finally {
try {
JDBCUtil.closeAll(null, null, con);
} catch (SQLException e) {
logger.warn("Cannot close (ignored): " + e.getMessage(), e);
}
}
}
private void listResourceType
(List result, Connection con, String queryKey)
throws RemoteException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = con.prepareStatement(props.getProperty(queryKey));
rs = ps.executeQuery();
while (rs.next()) {
WfResource resource = null;
if (queryKey.equals("allUsersQuery")) {
resource = new DefaultUserResource
(rasCtx, rs.getString(1), rs.getString(2));
} else if (queryKey.equals("allGroupsQuery")) {
resource = new DefaultGroupResource
(rasCtx, rs.getString(1), rs.getString(2));
} else {
resource = new DefaultRoleResource
(rasCtx, rs.getString(1), rs.getString(2));
}
result.add (resource);
}
} catch (SQLException e) {
throw new RemoteException (e.getMessage(), e);
} finally {
try {
JDBCUtil.closeAll(rs, ps, null);
} catch (SQLException e) {
logger.warn("Cannot close (ignored): " + e.getMessage(), e);
}
}
}
/**
* The <code>resSel</code> parameter is evaluated if it is of type
* <code>java.lang.String</code> only. It takes the
* following format:
* <dl>
* <dt><code>R:<i>role name</i></code></dt>
* <dd>Selects the role with the given name.</dd>
* <dt><code>G:<i>group name</i></code></dt>
* <dd>Selects the group with the given name.</dd>
* <dt><code>M:<i>member id</i></code></dt>
* <dd>Selects the member with the given id.</dd>
* </dl>
*
* @param resSel an object that describes resource selection criteria.
* @return collection of <code>WfResource</code> objects.
* @throws RemoteException if a system-level error occurs.
* @throws UnsupportedOperationException if the resource management
* service does not support this feature.
*/
public Collection selectResources (Object resSel)
throws RemoteException, UnsupportedOperationException {
Collection res = new ArrayList();
if (resSel == null || !(resSel instanceof String)) {
return res;
}
String crit = (String)resSel;
String resType = crit.substring(0, 1);
String resAccount = crit.substring(2);
Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
con = ds().getConnection();
if (resType.equals("M")) {
ps = con.prepareStatement(props.getProperty("userLookupQuery"));
ps.setString(1, resAccount);
rs = ps.executeQuery();
if (rs.next()) {
res.add(new DefaultUserResource
(rasCtx, rs.getString(1), rs.getString(2)));
}
} else if (resType.equals("G")) {
ps = con.prepareStatement
(props.getProperty("groupLookupQuery"));
ps.setString(1, resAccount);
rs = ps.executeQuery();
if (rs.next()) {
res.add(new DefaultGroupResource
(rasCtx, rs.getString(1), rs.getString(2)));
}
} else if (resType.equals("R")) {
ps = con.prepareStatement(props.getProperty("roleLookupQuery"));
ps.setString(1, resAccount);
rs = ps.executeQuery();
if (rs.next()) {
res.add(new DefaultRoleResource
(rasCtx, rs.getString(1), rs.getString(2)));
}
}
} catch (SQLException e) {
throw new RemoteException (e.getMessage(), e);
} finally {
try {
JDBCUtil.closeAll(rs, ps, con);
} catch (SQLException e) {
logger.warn("Cannot close (ignored): " + e.getMessage(), e);
}
}
return res;
}
}