001: /*
002: * This file is part of the WfMOpen project.
003: * Copyright (C) 2001-2005 Danet GmbH (www.danet.de), BU BTS.
004: * All rights reserved.
005: *
006: * This program is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU General Public License as published by
008: * the Free Software Foundation; either version 2 of the License, or
009: * (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: * $Id: DatabaseRmsService.java,v 1.10 2006/12/20 10:30:08 drmlipp Exp $
021: *
022: * $Log: DatabaseRmsService.java,v $
023: * Revision 1.10 2006/12/20 10:30:08 drmlipp
024: * Fixed id handling.
025: *
026: * Revision 1.9 2006/12/12 10:03:10 drmlipp
027: * Fixed authorizers.
028: *
029: * Revision 1.8 2006/10/15 19:29:51 mlipp
030: * Merged changes from 1.4.x up to 1.4ea3pre1.
031: *
032: * Revision 1.7.2.1 2006/10/14 21:34:05 mlipp
033: * Simplified resource assignment service implementation.
034: *
035: * Revision 1.7 2006/10/03 11:13:05 mlipp
036: * Improved.
037: *
038: * Revision 1.6 2006/10/02 20:54:32 mlipp
039: * Adapted to new resource base classes.
040: *
041: * Revision 1.5 2006/09/29 14:01:17 drmlipp
042: * Improved property names.
043: *
044: * Revision 1.4 2006/09/29 12:32:11 drmlipp
045: * Consistently using WfMOpen as projct name now.
046: *
047: * Revision 1.3 2006/09/29 11:39:16 drmlipp
048: * Various fixes.
049: *
050: * Revision 1.2 2006/09/28 21:28:09 mlipp
051: * Implementation continued.
052: *
053: * Revision 1.1 2006/09/28 15:03:56 drmlipp
054: * Getting started with db RMS.
055: *
056: */
057: package de.danet.an.workflow.rmsimpls.dbrms;
058:
059: import java.rmi.RemoteException;
060: import java.security.Principal;
061: import java.sql.Connection;
062: import java.sql.PreparedStatement;
063: import java.sql.ResultSet;
064: import java.sql.SQLException;
065: import java.util.ArrayList;
066: import java.util.Collection;
067: import java.util.List;
068: import java.util.Properties;
069:
070: import javax.naming.NamingException;
071: import javax.sql.DataSource;
072:
073: import de.danet.an.util.EJBUtil;
074: import de.danet.an.util.JDBCUtil;
075: import de.danet.an.workflow.omgcore.WfResource;
076: import de.danet.an.workflow.spis.rms.DefaultGroupResource;
077: import de.danet.an.workflow.spis.rms.DefaultRoleResource;
078: import de.danet.an.workflow.spis.rms.DefaultUserResource;
079: import de.danet.an.workflow.spis.rms.FactoryConfigurationError;
080: import de.danet.an.workflow.spis.rms.ResourceAssignmentContext;
081: import de.danet.an.workflow.spis.rms.ResourceManagementService;
082: import de.danet.an.workflow.spis.rms.ResourceNotFoundException;
083:
084: /**
085: * Implements the {@link
086: * de.danet.an.workflow.spis.rms.ResourceManagementService resource
087: * management service} based on an database.
088: *
089: * @author <a href="mailto:lipp@danet.de">Michael Lipp</a>
090: * @version $Revision: 1.10 $
091: */
092:
093: public class DatabaseRmsService implements ResourceManagementService {
094:
095: private static final org.apache.commons.logging.Log logger = org.apache.commons.logging.LogFactory
096: .getLog(DatabaseRmsService.class);
097:
098: private ResourceAssignmentContext rasCtx = null;
099: private Properties props = null;
100:
101: /**
102: * Constructs a new resource management service.
103: *
104: * @param props the configuration properties
105: * @param rasvc the resource assignment service
106: * @throws FactoryConfigurationError if the required resources
107: * cannot be obtained
108: */
109: public DatabaseRmsService(Properties props,
110: ResourceAssignmentContext rasCtx)
111: throws FactoryConfigurationError {
112: this .props = props;
113: this .rasCtx = rasCtx;
114: }
115:
116: private DataSource ds() {
117: try {
118: return (DataSource) EJBUtil.lookupJNDIEntry(props
119: .getProperty("dataSource"));
120: } catch (NamingException e) {
121: throw (IllegalStateException) (new IllegalStateException(
122: "Configured data source "
123: + props.getProperty("dataSource")
124: + " is unknown: " + e.getMessage()))
125: .initCause(e);
126: }
127: }
128:
129: /**
130: * Given a {@link java.security.Principal principal}, return the
131: * workflow resource associated with this principal by the
132: * resource management facility.<P>
133: *
134: * @param principal the principal.
135: * @return a <code>WfResource</code> object corresponding to the
136: * given principal.
137: * @throws ResourceNotFoundException if the StaffMember with the given key
138: * can't be found or the key is not associate with an StaffMember object.
139: * @throws RemoteException if a system-level error occurs.
140: */
141: public WfResource asResource(Principal principal)
142: throws ResourceNotFoundException, RemoteException {
143: Connection con = null;
144: PreparedStatement ps = null;
145: ResultSet rs = null;
146: try {
147: con = ds().getConnection();
148: ps = con.prepareStatement(props
149: .getProperty("userLookupQuery"));
150: ps.setString(1, principal.getName());
151: rs = ps.executeQuery();
152: if (!rs.next()) {
153: throw new ResourceNotFoundException("No such user: "
154: + principal.getName());
155: }
156: return new DefaultUserResource(rasCtx, rs.getString(1), rs
157: .getString(2));
158: } catch (SQLException e) {
159: throw new RemoteException(e.getMessage(), e);
160: } finally {
161: try {
162: JDBCUtil.closeAll(rs, ps, con);
163: } catch (SQLException e) {
164: logger.warn(
165: "Cannot close (ignored): " + e.getMessage(), e);
166: }
167: }
168: }
169:
170: /* Comment copied from interface */
171: public Collection authorizers(WfResource wfResource)
172: throws RemoteException {
173: List result = new ArrayList();
174: if (!(wfResource instanceof DefaultUserResource)) {
175: return result;
176: }
177: String userId = ((DefaultUserResource) wfResource).getId();
178: Connection con = null;
179: PreparedStatement ps = null;
180: ResultSet rs = null;
181: try {
182: con = ds().getConnection();
183: ps = con.prepareStatement(props.getProperty("rolesQuery"));
184: ps.setString(1, userId);
185: rs = ps.executeQuery();
186: while (rs.next()) {
187: result.add(new DefaultRoleResource(rasCtx, rs
188: .getString(1), rs.getString(2)));
189:
190: }
191: rs.close();
192: rs = null;
193: ps.close();
194: ps = null;
195: ps = con.prepareStatement(props.getProperty("groupsQuery"));
196: ps.setString(1, userId);
197: rs = ps.executeQuery();
198: while (rs.next()) {
199: result.add(new DefaultGroupResource(rasCtx, rs
200: .getString(1), rs.getString(2)));
201:
202: }
203: } catch (SQLException e) {
204: throw new RemoteException(e.getMessage(), e);
205: } finally {
206: try {
207: JDBCUtil.closeAll(rs, ps, con);
208: } catch (SQLException e) {
209: logger.warn(
210: "Cannot close (ignored): " + e.getMessage(), e);
211: }
212: }
213: return result;
214: }
215:
216: /* Comment copied from interface */
217: public WfResource resourceByKey(String key)
218: throws ResourceNotFoundException, RemoteException {
219: Connection con = null;
220: PreparedStatement ps = null;
221: ResultSet rs = null;
222: try {
223: con = ds().getConnection();
224: if (DefaultUserResource.isValidKey(key)) {
225: String resId = DefaultUserResource.getId(key);
226: ps = con.prepareStatement(props
227: .getProperty("userNameQuery"));
228: ps.setString(1, resId);
229: rs = ps.executeQuery();
230: if (!rs.next()) {
231: throw new ResourceNotFoundException(
232: "No user with id: " + resId);
233: }
234: return new DefaultUserResource(rasCtx, resId, rs
235: .getString(1));
236: } else if (DefaultGroupResource.isValidKey(key)) {
237: String resId = DefaultGroupResource.getId(key);
238: ps = con.prepareStatement(props
239: .getProperty("groupNameQuery"));
240: ps.setString(1, resId);
241: rs = ps.executeQuery();
242: if (!rs.next()) {
243: throw new ResourceNotFoundException(
244: "No group with id: " + resId);
245: }
246: return new DefaultGroupResource(rasCtx, resId, rs
247: .getString(1));
248: } else if (DefaultRoleResource.isValidKey(key)) {
249: String resId = DefaultRoleResource.getId(key);
250: ps = con.prepareStatement(props
251: .getProperty("roleNameQuery"));
252: ps.setString(1, resId);
253: rs = ps.executeQuery();
254: if (!rs.next()) {
255: throw new ResourceNotFoundException(
256: "No role with id: " + resId);
257: }
258: return new DefaultRoleResource(rasCtx, resId, rs
259: .getString(1));
260: } else {
261: throw new IllegalArgumentException(
262: "Resource with invalid key " + key);
263: }
264: } catch (SQLException e) {
265: throw new RemoteException(e.getMessage(), e);
266: } finally {
267: try {
268: JDBCUtil.closeAll(rs, ps, con);
269: } catch (SQLException e) {
270: logger.warn(
271: "Cannot close (ignored): " + e.getMessage(), e);
272: }
273: }
274: }
275:
276: /* Comment copied from interface. */
277: public Collection listResources() throws RemoteException {
278: Connection con = null;
279: try {
280: List result = new ArrayList();
281: con = ds().getConnection();
282: listResourceType(result, con, "allUsersQuery");
283: listResourceType(result, con, "allGroupsQuery");
284: listResourceType(result, con, "allRolesQuery");
285: return result;
286: } catch (SQLException e) {
287: throw new RemoteException(e.getMessage(), e);
288: } finally {
289: try {
290: JDBCUtil.closeAll(null, null, con);
291: } catch (SQLException e) {
292: logger.warn(
293: "Cannot close (ignored): " + e.getMessage(), e);
294: }
295: }
296: }
297:
298: private void listResourceType(List result, Connection con,
299: String queryKey) throws RemoteException {
300: PreparedStatement ps = null;
301: ResultSet rs = null;
302: try {
303: ps = con.prepareStatement(props.getProperty(queryKey));
304: rs = ps.executeQuery();
305: while (rs.next()) {
306: WfResource resource = null;
307: if (queryKey.equals("allUsersQuery")) {
308: resource = new DefaultUserResource(rasCtx, rs
309: .getString(1), rs.getString(2));
310: } else if (queryKey.equals("allGroupsQuery")) {
311: resource = new DefaultGroupResource(rasCtx, rs
312: .getString(1), rs.getString(2));
313: } else {
314: resource = new DefaultRoleResource(rasCtx, rs
315: .getString(1), rs.getString(2));
316: }
317: result.add(resource);
318: }
319: } catch (SQLException e) {
320: throw new RemoteException(e.getMessage(), e);
321: } finally {
322: try {
323: JDBCUtil.closeAll(rs, ps, null);
324: } catch (SQLException e) {
325: logger.warn(
326: "Cannot close (ignored): " + e.getMessage(), e);
327: }
328: }
329: }
330:
331: /**
332: * The <code>resSel</code> parameter is evaluated if it is of type
333: * <code>java.lang.String</code> only. It takes the
334: * following format:
335: * <dl>
336: * <dt><code>R:<i>role name</i></code></dt>
337: * <dd>Selects the role with the given name.</dd>
338: * <dt><code>G:<i>group name</i></code></dt>
339: * <dd>Selects the group with the given name.</dd>
340: * <dt><code>M:<i>member id</i></code></dt>
341: * <dd>Selects the member with the given id.</dd>
342: * </dl>
343: *
344: * @param resSel an object that describes resource selection criteria.
345: * @return collection of <code>WfResource</code> objects.
346: * @throws RemoteException if a system-level error occurs.
347: * @throws UnsupportedOperationException if the resource management
348: * service does not support this feature.
349: */
350: public Collection selectResources(Object resSel)
351: throws RemoteException, UnsupportedOperationException {
352: Collection res = new ArrayList();
353: if (resSel == null || !(resSel instanceof String)) {
354: return res;
355: }
356: String crit = (String) resSel;
357: String resType = crit.substring(0, 1);
358: String resAccount = crit.substring(2);
359:
360: Connection con = null;
361: PreparedStatement ps = null;
362: ResultSet rs = null;
363: try {
364: con = ds().getConnection();
365: if (resType.equals("M")) {
366: ps = con.prepareStatement(props
367: .getProperty("userLookupQuery"));
368: ps.setString(1, resAccount);
369: rs = ps.executeQuery();
370: if (rs.next()) {
371: res.add(new DefaultUserResource(rasCtx, rs
372: .getString(1), rs.getString(2)));
373: }
374: } else if (resType.equals("G")) {
375: ps = con.prepareStatement(props
376: .getProperty("groupLookupQuery"));
377: ps.setString(1, resAccount);
378: rs = ps.executeQuery();
379: if (rs.next()) {
380: res.add(new DefaultGroupResource(rasCtx, rs
381: .getString(1), rs.getString(2)));
382: }
383: } else if (resType.equals("R")) {
384: ps = con.prepareStatement(props
385: .getProperty("roleLookupQuery"));
386: ps.setString(1, resAccount);
387: rs = ps.executeQuery();
388: if (rs.next()) {
389: res.add(new DefaultRoleResource(rasCtx, rs
390: .getString(1), rs.getString(2)));
391: }
392: }
393: } catch (SQLException e) {
394: throw new RemoteException(e.getMessage(), e);
395: } finally {
396: try {
397: JDBCUtil.closeAll(rs, ps, con);
398: } catch (SQLException e) {
399: logger.warn(
400: "Cannot close (ignored): " + e.getMessage(), e);
401: }
402: }
403: return res;
404: }
405:
406: }
|