001 /*
002 Licensed to the Apache Software Foundation (ASF) under one
003 or more contributor license agreements. See the NOTICE file
004 distributed with this work for additional information
005 regarding copyright ownership. The ASF licenses this file
006 to you under the Apache License, Version 2.0 (the
007 "License"); you may not use this file except in compliance
008 with the License. You may obtain a copy of the License at
009
010 http://www.apache.org/licenses/LICENSE-2.0
011
012 Unless required by applicable law or agreed to in writing,
013 software distributed under the License is distributed on an
014 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 KIND, either express or implied. See the License for the
016 specific language governing permissions and limitations
017 under the License.
018 */
019 package org.apache.wiki.workflow;
020
021 import java.io.Serializable;
022 import java.security.Principal;
023 import java.util.ArrayList;
024 import java.util.Collection;
025 import java.util.LinkedList;
026
027 import org.apache.wiki.api.exceptions.WikiException;
028 import org.apache.wiki.WikiSession;
029
030 /**
031 * Keeps a queue of pending Decisions that need to be acted on by named
032 * Principals.
033 *
034 * @since 2.5
035 */
036 public class DecisionQueue implements Serializable
037 {
038 private static final long serialVersionUID = -7172912793410302533L;
039
040 private LinkedList<Decision> m_queue = new LinkedList<Decision>();
041
042 private volatile int m_next;
043
044 /**
045 * Constructs a new DecisionQueue.
046 */
047 public DecisionQueue()
048 {
049 m_next = 1000;
050 }
051
052 /**
053 * Adds a Decision to the DecisionQueue; also sets the Decision's unique
054 * identifier.
055 *
056 * @param decision
057 * the Decision to add
058 */
059 protected synchronized void add( Decision decision )
060 {
061 m_queue.addLast( decision );
062 decision.setId( nextId() );
063 }
064
065 /**
066 * Protected method that returns all pending Decisions in the queue, in
067 * order of submission. If no Decisions are pending, this method returns a
068 * zero-length array.
069 *
070 * @return the pending decisions TODO: explore whether this method could be
071 * made protected
072 */
073 protected Decision[] decisions()
074 {
075 return m_queue.toArray( new Decision[m_queue.size()] );
076 }
077
078 /**
079 * Protected method that removes a Decision from the queue.
080 * @param decision the decision to remove
081 */
082 protected synchronized void remove(Decision decision)
083 {
084 m_queue.remove( decision );
085 }
086
087 /**
088 * Returns a Collection representing the current Decisions that pertain to a
089 * users's WikiSession. The Decisions are obtained by iterating through the
090 * WikiSession's Principals and selecting those Decisions whose
091 * {@link Decision#getActor()} value match. If the wiki session is not
092 * authenticated, this method returns an empty Collection.
093 *
094 * @param session
095 * the wiki session
096 * @return the collection of Decisions, which may be empty
097 */
098 public Collection getActorDecisions(WikiSession session)
099 {
100 ArrayList<Decision> decisions = new ArrayList<Decision>();
101 if ( session.isAuthenticated() )
102 {
103 Principal[] principals = session.getPrincipals();
104 Principal[] rolePrincipals = session.getRoles();
105 for ( Decision decision : m_queue )
106 {
107 // Iterate through the Principal set
108 for ( Principal principal : principals )
109 {
110 if ( principal.equals( decision.getActor() ) )
111 {
112 decisions.add( decision );
113 }
114 }
115 // Iterate through the Role set
116 for ( Principal principal : rolePrincipals )
117 {
118 if ( principal.equals( decision.getActor() ) )
119 {
120 decisions.add( decision );
121 }
122 }
123 }
124 }
125 return decisions;
126 }
127
128 /**
129 * Attempts to complete a Decision by calling
130 * {@link Decision#decide(Outcome)}. This will cause the Step immediately
131 * following the Decision (if any) to start. If the decision completes
132 * successfully, this method also removes the completed decision from the
133 * queue.
134 *
135 * @param decision the Decision for which the Outcome will be supplied
136 * @param outcome the Outcome of the Decision
137 * @throws WikiException if the succeeding Step cannot start
138 * for any reason
139 */
140 public void decide( Decision decision, Outcome outcome ) throws WikiException
141 {
142 decision.decide( outcome );
143 if ( decision.isCompleted() )
144 {
145 remove( decision );
146 }
147
148 // TODO: We should fire an event indicating the Outcome, and whether the
149 // Decision completed successfully
150 }
151
152 /**
153 * Reassigns the owner of the Decision to a new owner. Under the covers,
154 * this method calls {@link Decision#reassign(Principal)}.
155 *
156 * @param decision the Decision to reassign
157 * @param owner the new owner
158 * @throws WikiException never
159 */
160 public synchronized void reassign(Decision decision, Principal owner) throws WikiException
161 {
162 if (decision.isReassignable())
163 {
164 decision.reassign( owner );
165
166 // TODO: We should fire an event indicating the reassignment
167 return;
168 }
169 throw new IllegalStateException( "Reassignments not allowed for this decision." );
170 }
171
172 /**
173 * Returns the next available unique identifier, which is subsequently
174 * incremented.
175 *
176 * @return the id
177 */
178 private synchronized int nextId()
179 {
180 int current = m_next;
181 m_next++;
182 return current;
183 }
184
185 }