001: /*
002: JSPWiki - a JSP-based WikiWiki clone.
003:
004: Copyright (C) 2001-2007 Janne Jalkanen (Janne.Jalkanen@iki.fi)
005:
006: This program is free software; you can redistribute it and/or modify
007: it under the terms of the GNU Lesser General Public License as published by
008: the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
015:
016: You should have received a copy of the GNU Lesser 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: package com.ecyrd.jspwiki.workflow;
021:
022: import java.security.Principal;
023: import java.util.ArrayList;
024: import java.util.Collection;
025: import java.util.Iterator;
026: import java.util.LinkedList;
027:
028: import com.ecyrd.jspwiki.WikiException;
029: import com.ecyrd.jspwiki.WikiSession;
030:
031: /**
032: * Keeps a queue of pending Decisions that need to be acted on by named
033: * Principals.
034: *
035: * @author Andrew Jaquith
036: * @since 2.5
037: */
038: public class DecisionQueue {
039:
040: private LinkedList m_queue = new LinkedList();
041:
042: private volatile int m_next;
043:
044: /**
045: * Constructs a new DecisionQueue.
046: */
047: public DecisionQueue() {
048: m_next = 1000;
049: }
050:
051: /**
052: * Adds a Decision to the DecisionQueue; also sets the Decision's unique
053: * identifier.
054: *
055: * @param decision
056: * the Decision to add
057: */
058: protected synchronized void add(Decision decision) {
059: m_queue.addLast(decision);
060: decision.setId(nextId());
061: }
062:
063: /**
064: * Protected method that returns all pending Decisions in the queue, in
065: * order of submission. If no Decisions are pending, this method returns a
066: * zero-length array.
067: *
068: * @return the pending decisions TODO: explore whether this method could be
069: * made protected
070: */
071: protected Decision[] decisions() {
072: return (Decision[]) m_queue
073: .toArray(new Decision[m_queue.size()]);
074: }
075:
076: /**
077: * Protected method that removes a Decision from the queue.
078: * @param decision the decision to remove
079: */
080: protected synchronized void remove(Decision decision) {
081: m_queue.remove(decision);
082: }
083:
084: /**
085: * Returns a Collection representing the current Decisions that pertain to a
086: * users's WikiSession. The Decisions are obtained by iterating through the
087: * WikiSession's Principals and selecting those Decisions whose
088: * {@link Decision#getActor()} value match. If the wiki session is not
089: * authenticated, this method returns an empty Collection.
090: *
091: * @param session
092: * the wiki session
093: * @return the collection of Decisions, which may be empty
094: */
095: public Collection getActorDecisions(WikiSession session) {
096: ArrayList decisions = new ArrayList();
097: if (session.isAuthenticated()) {
098: Principal[] principals = session.getPrincipals();
099: Principal[] rolePrincipals = session.getRoles();
100: for (Iterator it = m_queue.iterator(); it.hasNext();) {
101: Decision decision = (Decision) it.next();
102:
103: // Iterate through the Principal set
104: for (int i = 0; i < principals.length; i++) {
105: if (principals[i].equals(decision.getActor())) {
106: decisions.add(decision);
107: }
108: }
109: // Iterate through the Role set
110: for (int i = 0; i < rolePrincipals.length; i++) {
111: if (rolePrincipals[i].equals(decision.getActor())) {
112: decisions.add(decision);
113: }
114: }
115: }
116: }
117: return decisions;
118: }
119:
120: /**
121: * Attempts to complete a Decision by calling
122: * {@link Decision#decide(Outcome)}. This will cause the Step immediately
123: * following the Decision (if any) to start. If the decision completes
124: * successfully, this method also removes the completed decision from the
125: * queue.
126: *
127: * @param decision the Decision for which the Outcome will be supplied
128: * @param outcome the Outcome of the Decision
129: * @throws WikiException if the succeeding Step cannot start
130: * for any reason
131: */
132: public void decide(Decision decision, Outcome outcome)
133: throws WikiException {
134: decision.decide(outcome);
135: if (decision.isCompleted()) {
136: remove(decision);
137: }
138:
139: // TODO: We should fire an event indicating the Outcome, and whether the
140: // Decision completed successfully
141: }
142:
143: /**
144: * Reassigns the owner of the Decision to a new owner. Under the covers,
145: * this method calls {@link Decision#reassign(Principal)}.
146: *
147: * @param decision the Decision to reassign
148: * @param owner the new owner
149: * @throws WikiException never
150: */
151: public synchronized void reassign(Decision decision, Principal owner)
152: throws WikiException {
153: if (decision.isReassignable()) {
154: decision.reassign(owner);
155:
156: // TODO: We should fire an event indicating the reassignment
157: return;
158: }
159: throw new IllegalStateException(
160: "Reassignments not allowed for this decision.");
161: }
162:
163: /**
164: * Returns the next available unique identifier, which is subsequently
165: * incremented.
166: *
167: * @return the id
168: */
169: private synchronized int nextId() {
170: int current = m_next;
171: m_next++;
172: return current;
173: }
174:
175: }
|