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 */ 019package org.apache.wiki.workflow; 020 021import java.io.Serializable; 022import java.security.Principal; 023import java.util.ArrayList; 024import java.util.Collection; 025import java.util.LinkedList; 026 027import org.apache.wiki.api.exceptions.WikiException; 028import 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 */ 036public 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 071 */ 072 protected Decision[] decisions() 073 { 074 return m_queue.toArray( new Decision[m_queue.size()] ); 075 } 076 077 /** 078 * Protected method that removes a Decision from the queue. 079 * @param decision the decision to remove 080 */ 081 protected synchronized void remove(Decision decision) 082 { 083 m_queue.remove( decision ); 084 } 085 086 /** 087 * Returns a Collection representing the current Decisions that pertain to a 088 * users's WikiSession. The Decisions are obtained by iterating through the 089 * WikiSession's Principals and selecting those Decisions whose 090 * {@link Decision#getActor()} value match. If the wiki session is not 091 * authenticated, this method returns an empty Collection. 092 * 093 * @param session 094 * the wiki session 095 * @return the collection of Decisions, which may be empty 096 */ 097 public Collection<Decision> getActorDecisions(WikiSession session) 098 { 099 ArrayList<Decision> decisions = new ArrayList<>(); 100 if ( session.isAuthenticated() ) 101 { 102 Principal[] principals = session.getPrincipals(); 103 Principal[] rolePrincipals = session.getRoles(); 104 for ( Decision decision : m_queue ) 105 { 106 // Iterate through the Principal set 107 for ( Principal principal : principals ) 108 { 109 if ( principal.equals( decision.getActor() ) ) 110 { 111 decisions.add( decision ); 112 } 113 } 114 // Iterate through the Role set 115 for ( Principal principal : rolePrincipals ) 116 { 117 if ( principal.equals( decision.getActor() ) ) 118 { 119 decisions.add( decision ); 120 } 121 } 122 } 123 } 124 return decisions; 125 } 126 127 /** 128 * Attempts to complete a Decision by calling 129 * {@link Decision#decide(Outcome)}. This will cause the Step immediately 130 * following the Decision (if any) to start. If the decision completes 131 * successfully, this method also removes the completed decision from the 132 * queue. 133 * 134 * @param decision the Decision for which the Outcome will be supplied 135 * @param outcome the Outcome of the Decision 136 * @throws WikiException if the succeeding Step cannot start 137 * for any reason 138 */ 139 public void decide( Decision decision, Outcome outcome ) throws WikiException 140 { 141 decision.decide( outcome ); 142 if ( decision.isCompleted() ) 143 { 144 remove( decision ); 145 } 146 147 // TODO: We should fire an event indicating the Outcome, and whether the 148 // Decision completed successfully 149 } 150 151 /** 152 * Reassigns the owner of the Decision to a new owner. Under the covers, 153 * this method calls {@link Decision#reassign(Principal)}. 154 * 155 * @param decision the Decision to reassign 156 * @param owner the new owner 157 * @throws WikiException never 158 */ 159 public synchronized void reassign(Decision decision, Principal owner) throws WikiException 160 { 161 if (decision.isReassignable()) 162 { 163 decision.reassign( owner ); 164 165 // TODO: We should fire an event indicating the reassignment 166 return; 167 } 168 throw new IllegalStateException( "Reassignments not allowed for this decision." ); 169 } 170 171 /** 172 * Returns the next available unique identifier, which is subsequently 173 * incremented. 174 * 175 * @return the id 176 */ 177 private synchronized int nextId() 178 { 179 int current = m_next; 180 m_next++; 181 return current; 182 } 183 184}