/* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package org.apache.wiki.workflow; import java.io.Serializable; import java.security.Principal; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import org.apache.wiki.api.exceptions.WikiException; import org.apache.wiki.WikiSession; /** * Keeps a queue of pending Decisions that need to be acted on by named * Principals. * * @since 2.5 */ public class DecisionQueue implements Serializable { private static final long serialVersionUID = -7172912793410302533L; private LinkedList<Decision> m_queue = new LinkedList<Decision>(); private volatile int m_next; /** * Constructs a new DecisionQueue. */ public DecisionQueue() { m_next = 1000; } /** * Adds a Decision to the DecisionQueue; also sets the Decision's unique * identifier. * * @param decision * the Decision to add */ protected synchronized void add( Decision decision ) { m_queue.addLast( decision ); decision.setId( nextId() ); } /** * Protected method that returns all pending Decisions in the queue, in * order of submission. If no Decisions are pending, this method returns a * zero-length array. * * @return the pending decisions TODO: explore whether this method could be * made protected */ protected Decision[] decisions() { return m_queue.toArray( new Decision[m_queue.size()] ); } /** * Protected method that removes a Decision from the queue. * @param decision the decision to remove */ protected synchronized void remove(Decision decision) { m_queue.remove( decision ); } /** * Returns a Collection representing the current Decisions that pertain to a * users's WikiSession. The Decisions are obtained by iterating through the * WikiSession's Principals and selecting those Decisions whose * {@link Decision#getActor()} value match. If the wiki session is not * authenticated, this method returns an empty Collection. * * @param session * the wiki session * @return the collection of Decisions, which may be empty */ public Collection getActorDecisions(WikiSession session) { ArrayList<Decision> decisions = new ArrayList<Decision>(); if ( session.isAuthenticated() ) { Principal[] principals = session.getPrincipals(); Principal[] rolePrincipals = session.getRoles(); for ( Decision decision : m_queue ) { // Iterate through the Principal set for ( Principal principal : principals ) { if ( principal.equals( decision.getActor() ) ) { decisions.add( decision ); } } // Iterate through the Role set for ( Principal principal : rolePrincipals ) { if ( principal.equals( decision.getActor() ) ) { decisions.add( decision ); } } } } return decisions; } /** * Attempts to complete a Decision by calling * {@link Decision#decide(Outcome)}. This will cause the Step immediately * following the Decision (if any) to start. If the decision completes * successfully, this method also removes the completed decision from the * queue. * * @param decision the Decision for which the Outcome will be supplied * @param outcome the Outcome of the Decision * @throws WikiException if the succeeding Step cannot start * for any reason */ public void decide( Decision decision, Outcome outcome ) throws WikiException { decision.decide( outcome ); if ( decision.isCompleted() ) { remove( decision ); } // TODO: We should fire an event indicating the Outcome, and whether the // Decision completed successfully } /** * Reassigns the owner of the Decision to a new owner. Under the covers, * this method calls {@link Decision#reassign(Principal)}. * * @param decision the Decision to reassign * @param owner the new owner * @throws WikiException never */ public synchronized void reassign(Decision decision, Principal owner) throws WikiException { if (decision.isReassignable()) { decision.reassign( owner ); // TODO: We should fire an event indicating the reassignment return; } throw new IllegalStateException( "Reassignments not allowed for this decision." ); } /** * Returns the next available unique identifier, which is subsequently * incremented. * * @return the id */ private synchronized int nextId() { int current = m_next; m_next++; return current; } }