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 }