001: /*
002: * This file is part of the WfMOpen project.
003: * Copyright (C) 2001-2003 Danet GmbH (www.danet.de), GS-AN.
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: StandardResourceAssignmentService.java,v 1.8 2007/03/27 21:59:43 mlipp Exp $
021: *
022: * $Log: StandardResourceAssignmentService.java,v $
023: * Revision 1.8 2007/03/27 21:59:43 mlipp
024: * Fixed lots of checkstyle warnings.
025: *
026: * Revision 1.7 2007/02/27 14:34:18 drmlipp
027: * Some refactoring to reduce cyclic dependencies.
028: *
029: * Revision 1.6 2006/10/15 19:29:51 mlipp
030: * Merged changes from 1.4.x up to 1.4ea3pre1.
031: *
032: * Revision 1.5.2.2 2006/10/15 18:25:12 mlipp
033: * Fixed visibility.
034: *
035: * Revision 1.5.2.1 2006/10/14 21:34:06 mlipp
036: * Simplified resource assignment service implementation.
037: *
038: * Revision 1.5 2006/09/29 12:32:12 drmlipp
039: * Consistently using WfMOpen as projct name now.
040: *
041: * Revision 1.4 2005/10/15 21:19:38 mlipp
042: * Added support for providing WfAssignment
043: * implementations based purely on methods of Activity.
044: *
045: * Revision 1.3 2005/08/17 21:15:31 mlipp
046: * Synchronized with 1.3.1p3.
047: *
048: * Revision 1.1.1.2.6.1 2005/08/16 14:04:04 drmlipp
049: * Backported LDAP RMS and security role fixes.
050: *
051: * Revision 1.1.1.2.4.2 2005/08/15 16:38:13 drmlipp
052: * Improved sealing of exceptions from RMS.
053: *
054: * Revision 1.1.1.2.4.1 2005/08/14 21:07:55 drmlipp
055: * Fixed initialization problem (RMS initialized in wrong context).
056: *
057: * Revision 1.2 2005/07/04 20:40:25 mlipp
058: * Improved problem reporting.
059: *
060: * Revision 1.1.1.2 2004/08/18 15:17:36 drmlipp
061: * Update to 1.2
062: *
063: * Revision 1.28 2004/06/14 19:37:19 lipp
064: * Fixed assignment functions and cleaned up assignment related
065: * interfaces.
066: *
067: * Revision 1.27 2004/03/03 17:23:13 lipp
068: * Implemented setAssignee and cleaned up code a bit.
069: *
070: * Revision 1.26 2003/06/27 08:51:46 lipp
071: * Fixed copyright/license information.
072: *
073: * Revision 1.25 2003/04/26 16:11:15 lipp
074: * Moved some classes to reduce package dependencies.
075: *
076: * Revision 1.24 2003/04/25 14:50:59 lipp
077: * Fixed javadoc errors and warnings.
078: *
079: * Revision 1.23 2002/12/19 21:37:43 lipp
080: * Reorganized interfaces.
081: *
082: * Revision 1.22 2002/11/26 11:23:30 lipp
083: * Modified RemoteException comment.
084: *
085: * Revision 1.21 2002/06/27 10:55:35 lipp
086: * Delayed initialization even more.
087: *
088: * Revision 1.20 2002/01/23 15:22:53 huaiyang
089: * Add M:<name> as resource selection in autoAssignResources method.
090: *
091: * Revision 1.19 2002/01/15 13:42:21 lipp
092: * Added resourceByKey to ResourceAssignmentService and some missing
093: * NoSuchResourceExceptions in various methods.
094: *
095: * Revision 1.18 2002/01/15 11:25:59 huaiyang
096: * Add the assigned activity function to the mgmt client.
097: *
098: * Revision 1.17 2002/01/09 14:48:51 lipp
099: * Changed access to current user as resource.
100: *
101: * Revision 1.16 2002/01/09 14:00:01 lipp
102: * Cleaned up relation between wfcore, resource assignment and resource
103: * management service.
104: *
105: * Revision 1.15 2002/01/08 18:12:06 lipp
106: * Method to determined currently logged on user moved to resource
107: * management service.
108: *
109: * Revision 1.14 2001/12/20 22:32:06 lipp
110: * Removed no longer needed method.
111: *
112: * Revision 1.13 2001/12/20 22:27:34 lipp
113: * WfResource release semantics fixed.
114: *
115: * Revision 1.12 2001/12/20 22:22:50 lipp
116: * Resource release implemented.
117: *
118: * Revision 1.11 2001/12/20 16:24:25 lipp
119: * Resource assignment extended.
120: *
121: * Revision 1.10 2001/12/19 22:07:29 lipp
122: * Automatic resource assignment (workflow core part) implemented.
123: *
124: * Revision 1.9 2001/12/18 22:16:52 lipp
125: * Restructured DOM generation, implemented "assignments" method from ras.
126: *
127: * Revision 1.8 2001/12/18 15:35:11 lipp
128: * Implemented workItems and isMemberOfWorkItem.
129: *
130: * Revision 1.7 2001/12/17 19:56:03 lipp
131: * Implementation of equality checking improved.
132: *
133: * Revision 1.6 2001/12/17 15:50:57 lipp
134: * Better handling of resource refs.
135: *
136: * Revision 1.5 2001/12/17 12:14:04 lipp
137: * Adapted to configurable ResourceManagement/AssignmentServices.
138: *
139: * Revision 1.4 2001/12/17 10:52:19 lipp
140: * Added configurable resource management service.
141: *
142: * Revision 1.3 2001/12/17 09:15:50 lipp
143: * Javadoc fixes.
144: *
145: * Revision 1.2 2001/12/16 21:48:57 lipp
146: * addAssignment implemented.
147: *
148: * Revision 1.1 2001/12/16 10:37:35 lipp
149: * Assignment service implemented.
150: *
151: */
152: package de.danet.an.workflow.assignment;
153:
154: import java.util.ArrayList;
155: import java.util.Collection;
156: import java.util.HashMap;
157: import java.util.Iterator;
158: import java.util.Map;
159:
160: import java.io.IOException;
161: import java.io.OptionalDataException;
162: import java.rmi.RemoteException;
163: import java.security.Principal;
164: import java.sql.Connection;
165: import java.sql.PreparedStatement;
166: import java.sql.ResultSet;
167: import java.sql.SQLException;
168:
169: import javax.ejb.EJBException;
170: import javax.sql.DataSource;
171:
172: import de.danet.an.util.EJBUtil;
173: import de.danet.an.util.JDBCUtil;
174: import de.danet.an.util.ResourceNotAvailableException;
175: import de.danet.an.util.UniversalPrepStmt;
176: import de.danet.an.workflow.omgcore.InvalidResourceException;
177: import de.danet.an.workflow.omgcore.NotAssignedException;
178: import de.danet.an.workflow.omgcore.WfActivity;
179: import de.danet.an.workflow.omgcore.WfAssignment;
180: import de.danet.an.workflow.omgcore.WfResource;
181:
182: import de.danet.an.workflow.api.AlreadyAssignedException;
183: import de.danet.an.workflow.api.InvalidKeyException;
184: import de.danet.an.workflow.api.NoSuchResourceException;
185: import de.danet.an.workflow.api.Participant;
186: import de.danet.an.workflow.api.Participant.ParticipantType;
187:
188: import de.danet.an.workflow.spis.ras.ActivityFinder;
189: import de.danet.an.workflow.spis.ras.FactoryConfigurationError;
190: import de.danet.an.workflow.spis.ras.NoSuchActivityException;
191: import de.danet.an.workflow.spis.ras.ResourceAssignmentService;
192: import de.danet.an.workflow.spis.rms.ResourceAssignmentContext;
193: import de.danet.an.workflow.spis.rms.ResourceManagementService;
194: import de.danet.an.workflow.spis.rms.ResourceManagementServiceFactory;
195: import de.danet.an.workflow.spis.rms.ResourceNotFoundException;
196:
197: /**
198: * This class implements the standard resource assignment service provided
199: * as part of the workflow package.
200: */
201: public class StandardResourceAssignmentService implements
202: ResourceAssignmentService {
203:
204: private static final org.apache.commons.logging.Log logger = org.apache.commons.logging.LogFactory
205: .getLog(StandardResourceAssignmentService.class);
206:
207: /** Warned about RMS not supporting resource selection? */
208: private static boolean rmsUnsuppportedWarned = false;
209:
210: /** An in memory representation of the known finders. */
211: private static Map finderByDBIdCache = new HashMap(); // used for sync
212: private static Map dbIdByFinderCache = null; // indicates uninitialized
213:
214: /**
215: * The data source of the database.
216: * @see javax.sql.DataSource
217: */
218: private DataSource ds = null;
219:
220: /** The resource management service associated with this service. */
221: private ResourceManagementService rms = null;
222:
223: /**
224: * Constructs a new resource assignment service.
225: *
226: * @param rasCtx an implementation of the resource Assignment context
227: * @param rmsFac the resource management service factory.
228: * @throws FactoryConfigurationError if a problem creating the RMS
229: * occurs
230: */
231: public StandardResourceAssignmentService(
232: ResourceManagementServiceFactory rmsFac,
233: ResourceAssignmentContext rasCtx, DataSource ds)
234: throws FactoryConfigurationError {
235: try {
236: rmsFac.setResourceAssignmentContext(rasCtx);
237: rms = rmsFac.newResourceManagementService();
238: } catch (de.danet.an.workflow.spis.rms.FactoryConfigurationError e) {
239: throw (FactoryConfigurationError) (new FactoryConfigurationError(
240: "Cannot create RMS: " + e.getMessage()))
241: .initCause(e);
242: }
243: this .ds = ds;
244: }
245:
246: /**
247: * Return the data source oassed to the constructor.
248: * @return Returns the data source.
249: */
250: protected DataSource getDataSource() {
251: return ds;
252: }
253:
254: /**
255: * Return the resource management service passed to the constructor.
256: * @return Returns the rms.
257: */
258: protected ResourceManagementService getResourceManagementService() {
259: return rms;
260: }
261:
262: /**
263: * @throws EJBException
264: */
265: private void initFinderCache() throws EJBException {
266: synchronized (finderByDBIdCache) {
267: if (dbIdByFinderCache == null) {
268: // read finders from database. we cannot do lazy loading,
269: // because we cannot lookup by finder (being binary)
270: Map finderLookup = new HashMap();
271: Map dbIdLookup = new HashMap();
272: try {
273: Connection con = null;
274: PreparedStatement prepStmt = null;
275: ResultSet rs = null;
276: try {
277: con = ds.getConnection();
278: String selectStatement = "SELECT DBId, LocData FROM ActivityFinders";
279: prepStmt = con
280: .prepareStatement(selectStatement);
281: rs = prepStmt.executeQuery();
282: while (rs.next()) {
283: long idx = rs.getLong(1);
284: Object finder = JDBCUtil.getBinary(rs, 2);
285: finderLookup.put(new Long(idx), finder);
286: dbIdLookup.put(finder, new Long(idx));
287: }
288: } finally {
289: JDBCUtil.closeAll(rs, prepStmt, con);
290: }
291: } catch (SQLException se) {
292: throw new EJBException(se);
293: } catch (OptionalDataException ode) {
294: throw new EJBException(ode);
295: } catch (IOException ioe) {
296: throw new EJBException(ioe);
297: } catch (ClassNotFoundException cnf) {
298: throw new EJBException(cnf);
299: }
300: finderByDBIdCache = finderLookup;
301: dbIdByFinderCache = dbIdLookup;
302: }
303: }
304: }
305:
306: /**
307: * Get the finder id for a finder.
308: *
309: * @param finder the activity finder in question.
310: * @return the associated index.
311: * @throws RemoteException if a system-level error occurs.
312: */
313: protected long finderId(ActivityFinder finder)
314: throws RemoteException {
315: synchronized (finderByDBIdCache) {
316: initFinderCache();
317: Long idx = (Long) dbIdByFinderCache.get(finder);
318: if (idx != null) {
319: return idx.longValue();
320: }
321: // new finder, insert
322: try {
323: Connection con = ds.getConnection();
324: UniversalPrepStmt prepStmt = null;
325: long newIdx = EJBUtil.newPrimaryKey("ActivityFinders");
326: try {
327: prepStmt = new UniversalPrepStmt(ds, con,
328: "INSERT INTO ActivityFinders (DBId, LocData) "
329: + "VALUES (?, ?)");
330: prepStmt.setLong(1, newIdx);
331: prepStmt.setBinary(2, finder);
332: prepStmt.executeUpdate();
333: } finally {
334: JDBCUtil.closeAll(null, prepStmt, con);
335: }
336: finderByDBIdCache.put(new Long(newIdx), finder);
337: dbIdByFinderCache.put(finder, new Long(newIdx));
338: return newIdx;
339: } catch (ResourceNotAvailableException e) {
340: throw new EJBException(e);
341: } catch (SQLException se) {
342: throw new EJBException(se);
343: } catch (OptionalDataException ode) {
344: throw new EJBException(ode);
345: } catch (IOException ioe) {
346: throw new EJBException(ioe);
347: }
348: }
349: }
350:
351: /**
352: * Lookup a finder for a given finder id.
353: *
354: * @param finderId the finder id
355: * @return the finder
356: */
357: protected ActivityFinder finderByIndex(long finderId) {
358: initFinderCache();
359: return (ActivityFinder) finderByDBIdCache
360: .get(new Long(finderId));
361: }
362:
363: /**
364: * Change an assignment for enacting an activity.<P>
365: *
366: * @param finder the finder used to lookup activities by
367: * their <code>finderId</code>s
368: * @param actId a unique (with respect to an <code>ActivityFinder</code>)
369: * identifier for the Activity. The length of <code>actId</code> is
370: * guaranteed not to exceed 64
371: * @param activity the activity being enacted
372: * @param oldResource the resource that has its assignment removed
373: * @param newResource the resource to be assigned
374: * @throws RemoteException if a system-level error occurs
375: * @throws InvalidResourceException if the resource is invalid.
376: * As the environment is a concurrent multi user environment,
377: * <code>WfResource</code> objects may become invalid
378: * @throws AlreadyAssignedException if the assignment already
379: * exists
380: * @throws NotAssignedException if there is no assignment to the
381: * old resource
382: * @see ActivityFinder
383: */
384: public void changeAssignment(ActivityFinder finder, String actId,
385: WfActivity activity, WfResource oldResource,
386: WfResource newResource) throws RemoteException,
387: InvalidResourceException, AlreadyAssignedException,
388: NotAssignedException {
389: long finderId = finderId(finder);
390: try {
391: Connection con = null;
392: PreparedStatement prepStmt = null;
393: ResultSet rs = null;
394: try {
395: con = ds.getConnection();
396: prepStmt = con
397: .prepareStatement("UPDATE Assignments SET assignedresource=? WHERE "
398: + "actfinder=? AND activity=? AND assignedresource=?");
399: prepStmt.setString(1, newResource.resourceKey());
400: prepStmt.setLong(2, finderId);
401: prepStmt.setString(3, actId);
402: prepStmt.setString(4, oldResource.resourceKey());
403: if (prepStmt.executeUpdate() != 1) {
404: throw new NotAssignedException();
405: }
406: } finally {
407: JDBCUtil.closeAll(rs, prepStmt, con);
408: }
409: } catch (SQLException se) {
410: throw new EJBException(se);
411: }
412: }
413:
414: /**
415: * Remove the assignment of a resource to an activity. This method
416: * is called by the workflow engine to implement the assignment
417: * manipulation methods provided by its API.<P>
418: *
419: * @param actId a unique (with respect to an <code>ActivityFinder</code>)
420: * identifier for the Activity. The length of <code>actId</code> is
421: * guaranteed not to exceed 64.
422: * @param finder the finder used to lookup activities by
423: * their <code>finderId</code>s.
424: * @param activity the activity that is about to become ready.
425: * @param resource the resource to be assigned.
426: * @throws RemoteException if a system-level error occurs.
427: * @throws InvalidResourceException if the resource is invalid.
428: * @throws NotAssignedException if the resource is not assigned to
429: * the given activity
430: * @see ActivityFinder
431: */
432: public void removeAssignment(ActivityFinder finder, String actId,
433: WfActivity activity, WfResource resource)
434: throws RemoteException, InvalidResourceException,
435: NotAssignedException {
436: long finderId = finderId(finder);
437: try {
438: Connection con = null;
439: PreparedStatement prepStmt = null;
440: ResultSet rs = null;
441: try {
442: con = ds.getConnection();
443: prepStmt = con
444: .prepareStatement("DELETE FROM Assignments WHERE "
445: + "actfinder=? AND activity=? AND assignedresource=?");
446: prepStmt.setLong(1, finderId);
447: prepStmt.setString(2, actId);
448: prepStmt.setString(3, resource.resourceKey());
449: if (prepStmt.executeUpdate() != 1) {
450: throw new NotAssignedException();
451: }
452: } finally {
453: JDBCUtil.closeAll(rs, prepStmt, con);
454: }
455: } catch (SQLException se) {
456: throw new EJBException(se);
457: }
458: }
459:
460: /**
461: * Get the resource associated with an Assignment.<P>
462: *
463: * @param asnmnt the assignment
464: * @return the resource
465: * @throws RemoteException if a system-level error occurs.
466: * @see de.danet.an.workflow.spis.ras.ActivityFinder
467: * @ejb.interface-method view-type="remote"
468: */
469: public WfResource getResource(WfAssignment asnmnt)
470: throws RemoteException {
471: String resKey = null;
472: Connection con = null;
473: PreparedStatement prepStmt = null;
474: ResultSet rs = null;
475: try {
476: con = ds.getConnection();
477: try {
478: prepStmt = con
479: .prepareStatement("SELECT AssignedResource FROM Assignments WHERE DBId = ?");
480: prepStmt.setLong(1, ((Assignment) asnmnt).key());
481: rs = prepStmt.executeQuery();
482: if (!rs.next()) {
483: throw new IllegalStateException(
484: "Cannot access assignment info, "
485: + "assignment has probably been removed");
486: }
487: resKey = rs.getString(1);
488: } finally {
489: JDBCUtil.closeAll(rs, prepStmt, con);
490: }
491: } catch (SQLException se) {
492: throw new EJBException(se);
493: }
494: try {
495: return rms.resourceByKey(resKey);
496: } catch (ResourceNotFoundException rnf) {
497: logger.error(rnf.getMessage(), rnf);
498: throw new IllegalStateException("Cannot lookup resource, "
499: + "has probably been deleted while being assigned");
500: } catch (RemoteException e) {
501: throw e;
502: } catch (Exception e) {
503: logger.error(rms + " has thrown an unexpected exception "
504: + "(mapped to IllegalStateException): "
505: + e.getMessage(), e);
506: throw (IllegalStateException) (new IllegalStateException(
507: "Unexpected exception from RMS: " + e.getMessage()))
508: .initCause(e);
509: }
510: }
511:
512: /**
513: * Return the assignments to an activity.
514: *
515: * @param actId a unique (with respect to an <code>ActivityFinder</code>)
516: * identifier for the Activity. The length of <code>actId</code> is
517: * guaranteed not to exceed 64.
518: * @param finder the finder used to lookup activities by
519: * their <code>finderId</code>s.
520: * @param activity the activity.
521: * @return the collection of assignments (instances of
522: * {@link de.danet.an.workflow.omgcore.WfAssignment
523: * <code>WfAssignment</code>}).
524: * @throws RemoteException if a system-level error occurs.
525: */
526: public Collection assignments(ActivityFinder finder, String actId,
527: WfActivity activity) throws RemoteException {
528: Collection res = new ArrayList();
529: Connection con = null;
530: PreparedStatement prepStmt = null;
531: ResultSet rs = null;
532: try {
533: try {
534: con = ds.getConnection();
535: String selectStatement = "SELECT DBId FROM Assignments "
536: + "WHERE actfinder=? AND activity=?";
537: prepStmt = con.prepareStatement(selectStatement);
538: prepStmt.setLong(1, finderId(finder));
539: prepStmt.setString(2, actId);
540: rs = prepStmt.executeQuery();
541: while (rs.next()) {
542: res.add(new Assignment(rs.getLong(1), activity));
543: }
544: } finally {
545: JDBCUtil.closeAll(rs, prepStmt, con);
546: }
547: } catch (SQLException se) {
548: throw new EJBException(se);
549: }
550: return res;
551: }
552:
553: /**
554: * Return the assignments of a given resource.
555: *
556: * @param resource the resource.
557: * @return the collection of assigned work items (instances of
558: * {@link de.danet.an.workflow.omgcore.WfAssignment
559: * <code>WfAssignment</code>}).
560: * @throws RemoteException if a system-level error occurs.
561: * @see de.danet.an.workflow.spis.rms
562: */
563: public Collection workItems(WfResource resource)
564: throws RemoteException {
565: Connection con = null;
566: PreparedStatement prepStmt = null;
567: ResultSet rs = null;
568: Collection res = new ArrayList();
569: try {
570: try {
571: con = ds.getConnection();
572: prepStmt = con
573: .prepareStatement("SELECT DBId, actfinder, activity FROM Assignments "
574: + "WHERE assignedresource=?");
575: prepStmt.setString(1, resource.resourceKey());
576: rs = prepStmt.executeQuery();
577: while (rs.next()) {
578: long dbid = rs.getLong(1);
579: long idx = rs.getLong(2);
580: ActivityFinder finder = finderByIndex(idx);
581: String actKey = rs.getString(3);
582: try {
583: res.add(new Assignment(dbid, finder
584: .find(actKey)));
585: } catch (NoSuchActivityException e) {
586: // race condition
587: logger
588: .debug("Found assignment to activity key "
589: + actKey
590: + " but not the activity: "
591: + e.getMessage());
592: } catch (RemoteException rex) {
593: throw new EJBException(rex);
594: }
595: }
596: } finally {
597: JDBCUtil.closeAll(rs, prepStmt, con);
598: }
599: } catch (RemoteException e) {
600: throw new EJBException(e);
601: } catch (SQLException se) {
602: throw new EJBException(se);
603: }
604: return res;
605: }
606:
607: /**
608: * Find out if a given assignment belongs to the work items assigned to
609: * a particular resource.
610: *
611: * @param resource the resource.
612: * @param assignment the assignment in question.
613: * @return <code>true</code> if the <code>assignment</code> belongs to
614: * the work items of the <code>resource</code>.
615: * @throws RemoteException if a system-level error occurs.
616: * @throws NoSuchResourceException if the resource is invalid.
617: * @see de.danet.an.workflow.spis.rms
618: */
619: public boolean isMemberOfWorkItems(WfResource resource,
620: WfAssignment assignment) throws RemoteException,
621: NoSuchResourceException {
622: if (!(assignment instanceof Assignment)) {
623: return false;
624: }
625: Assignment assmnt = (Assignment) assignment;
626: return resource.equals(assmnt.assignee());
627: }
628:
629: /**
630: * Triggers the automatic assignment of resources to an activity that
631: * is about to become ready.<P>
632: *
633: * If <code>resSel</code> is of type string, the following cases
634: * are handled by the assignment service:
635: * <dl>
636: * <dt><code>!:currentUser</code></dt>
637: * <dd>Assigns the current user.</dd>
638: * </dl>
639: *
640: * In all other cases the parameter <code>resSel</code> is simply
641: * passed through to
642: * {@link ResourceManagementService#selectResources the resource
643: * selection service}.
644: *
645: * @param actId a unique (with respect to an <code>ActivityFinder</code>)
646: * identifier for the Activity. The length of <code>actId</code> is
647: * guaranteed not to exceed 64.
648: * @param finder the finder used to lookup activities by
649: * their <code>finderId</code>s.
650: * @param activity the activity that is about to become ready.
651: * @param principal the current caller as known in the EJB context,
652: * may be <code>null</code>.
653: * @param participant the <code>Participant</code> that describes the
654: * resource selection criteria.
655: * @return the assigned resources (instances of {@link
656: * de.danet.an.workflow.omgcore.WfResource
657: * <code>WfResource</code>}).
658: * @throws RemoteException if a system-level error occurs.
659: * @see ActivityFinder
660: */
661: public Collection autoAssignResources(ActivityFinder finder,
662: String actId, WfActivity activity, Principal principal,
663: Participant participant) throws RemoteException {
664: if (participant == null) {
665: return new ArrayList();
666: }
667:
668: Object resSel = null;
669: ParticipantType type = participant.getParticipantType();
670: Collection assigned = new ArrayList();
671: if (type.isResourceSet()) {
672: resSel = "G:" + participant.getName();
673: } else if (type.isRole()) {
674: resSel = "R:" + participant.getName();
675: } else if (type.isHuman()) {
676: String crit = (String) participant.getResourceSelection();
677: if (crit != null && crit.startsWith("!:currentUser")
678: && principal != null) {
679: try {
680: WfResource res = asResource(principal);
681: assigned.add(res);
682: addAssignment(finderId(finder), actId, res);
683: return assigned;
684: } catch (AlreadyAssignedException rnf) {
685: return assigned;
686: } catch (InvalidKeyException rnf) {
687: return assigned;
688: }
689: }
690: resSel = "M:" + participant.getName();
691: }
692:
693: // fall through
694: try {
695: Collection reses = rms.selectResources(resSel);
696: if (reses.size() == 0) {
697: return assigned;
698: }
699: long finderId = finderId(finder);
700: for (Iterator i = reses.iterator(); i.hasNext();) {
701: WfResource res = (WfResource) i.next();
702: assigned.add(res);
703: addAssignment(finderId, actId, res);
704: }
705: return assigned;
706: } catch (AlreadyAssignedException rnf) {
707: return assigned;
708: } catch (UnsupportedOperationException uo) {
709: if (!rmsUnsuppportedWarned) {
710: logger.warn("Configured RMS does not support resource "
711: + "selection, no resources assigned.");
712: rmsUnsuppportedWarned = true;
713: }
714: return assigned;
715: } catch (RemoteException e) {
716: throw e;
717: } catch (Exception e) {
718: logger
719: .error(rms
720: + " has thrown an unexpected exception, "
721: + "no resources are assigned: "
722: + e.getMessage(), e);
723: return assigned;
724: }
725: }
726:
727: /**
728: * Assign a resource to an activity.
729: *
730: * @param actId a unique (with respect to an <code>ActivityFinder</code>)
731: * identifier for the Activity. The length of <code>actId</code> is
732: * guaranteed not to exceed 64.
733: * @param finderId the <code>finderId</code>s.
734: * @param activity the activity that is about to become ready.
735: * @param resource the resource to be assigned.
736: * @throws RemoteException if a system-level error occurs.
737: * @throws NoSuchResourceException if the resource is invalid.
738: * @see ActivityFinder
739: * @throws AlreadyAssignedException if the assignment already
740: * exists
741: */
742: protected void addAssignment(long finderId, String actId,
743: WfResource resource) throws RemoteException,
744: AlreadyAssignedException {
745: try {
746: Connection con = null;
747: PreparedStatement prepStmt = null;
748: ResultSet rs = null;
749: try {
750: con = ds.getConnection();
751: prepStmt = con
752: .prepareStatement("SELECT DBId FROM Assignments WHERE "
753: + "actfinder=? AND activity=? AND assignedresource=?");
754: prepStmt.setLong(1, finderId);
755: prepStmt.setString(2, actId);
756: prepStmt.setString(3, resource.resourceKey());
757: rs = prepStmt.executeQuery();
758: if (rs.next()) {
759: throw new AlreadyAssignedException();
760: }
761: rs.close();
762: rs = null;
763: prepStmt.close();
764: prepStmt = con
765: .prepareStatement("INSERT INTO Assignments "
766: + "(DBId, actfinder, activity, assignedresource) "
767: + "VALUES (?, ?, ?, ?)");
768: long id = EJBUtil.newPrimaryKey("Assignments");
769: prepStmt.setLong(1, id);
770: prepStmt.setLong(2, finderId);
771: prepStmt.setString(3, actId);
772: prepStmt.setString(4, resource.resourceKey());
773: prepStmt.executeUpdate();
774: } catch (ResourceNotAvailableException e) {
775: throw new SQLException("Cannot get primary key"
776: + e.getMessage());
777: } finally {
778: JDBCUtil.closeAll(rs, prepStmt, con);
779: }
780: } catch (SQLException se) {
781: throw new EJBException(se);
782: }
783:
784: }
785:
786: /**
787: * Returns at least the collection of all the workflow resources whom has
788: * been assigned work items, but optionally it can return the additional
789: * workflow resources who are known to the resource assignment service.
790: *
791: * @return the collection of the known resources to the ras (instances of
792: * {@link de.danet.an.workflow.omgcore.WfResource
793: * <code>WfResource</code>}).
794: * @throws RemoteException if a system-level error occurs.
795: * @see de.danet.an.workflow.assignment
796: */
797: public Collection knownResources() throws RemoteException {
798: try {
799: return rms.listResources();
800: } catch (RemoteException e) {
801: throw e;
802: } catch (Exception e) {
803: logger.error(rms + " has thrown an unexpected exception, "
804: + "no resources are listed: " + e.getMessage(), e);
805: return new ArrayList();
806: }
807: }
808:
809: /* Comment copied from interface. */
810: public Collection authorizers(WfResource resource)
811: throws RemoteException {
812: try {
813: return rms.authorizers(resource);
814: } catch (RemoteException e) {
815: throw e;
816: } catch (Exception e) {
817: logger.error(rms + " has thrown an unexpected exception, "
818: + "no authorizers returned: " + e.getMessage(), e);
819: return new ArrayList();
820: }
821: }
822:
823: /**
824: * Given a {@link java.security.Principal principal}, return the
825: * workflow resource associated with this principal. This implementation
826: * simply delegates the request to the resource management service.
827: *
828: * @param principal the principal.
829: * @return a <code>WfResource</code> object corresponding to the
830: * given principal.
831: * @throws NoSuchResourceException if the StaffMember with the given key
832: * can't be found or the key is not associate with an StaffMember object.
833: * @throws RemoteException if a system-level error occurs.
834: */
835: public WfResource asResource(Principal principal)
836: throws RemoteException, InvalidKeyException {
837: try {
838: return rms.asResource(principal);
839: } catch (ResourceNotFoundException rnf) {
840: throw new InvalidKeyException(rnf.getMessage());
841: }
842: }
843:
844: /**
845: * Given the <code>key</code> of a <code>WfResource</code>
846: * (obtained with {@link WfResource#resourceKey
847: * <code>resourceKey()</code>}), return the workflow resource
848: * associated with this key.<P>
849: *
850: * This method is implemented by simply calling
851: * {@link ResourceManagementService#resourceByKey
852: * <code>resourceByKey</code>} of the underlying resource
853: * management service.
854: *
855: * @param key the key.
856: * @return a <code>WfResource</code> object corresponding to the
857: * given key.
858: * @throws NoSuchResourceException if the resource with the given
859: * key can't be found. As the environment is a concurrent multi
860: * user environment, <code>WfResource</code> objects (and keys obtained
861: * from <code>WfResource</code> objects) may become invalid.
862: * @throws RemoteException if a system-level error occurs.
863: */
864: public WfResource resourceByKey(String key)
865: throws InvalidKeyException, RemoteException {
866: try {
867: return rms.resourceByKey(key);
868: } catch (ResourceNotFoundException rnf) {
869: throw new InvalidKeyException(rnf.getMessage());
870: }
871: }
872: }
|