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 org.apache.wiki.api.core.Context; 022import org.apache.wiki.api.core.Session; 023import org.apache.wiki.api.exceptions.WikiException; 024import org.apache.wiki.event.WikiEventEmitter; 025import org.apache.wiki.event.WorkflowEvent; 026 027import java.io.Serializable; 028import java.security.Principal; 029import java.util.ArrayList; 030import java.util.Collection; 031import java.util.LinkedList; 032import java.util.concurrent.atomic.AtomicInteger; 033 034 035/** 036 * Keeps a queue of pending Decisions that need to be acted on by named Principals. 037 * 038 * @since 2.5 039 */ 040public class DecisionQueue implements Serializable { 041 042 private static final long serialVersionUID = -7172912793410302533L; 043 044 private final LinkedList< Decision > m_queue = new LinkedList<>(); 045 046 private final AtomicInteger next = new AtomicInteger( 1_000 ); 047 048 /** Constructs a new DecisionQueue. */ 049 public DecisionQueue() { 050 } 051 052 /** 053 * Adds a Decision to the DecisionQueue; also sets the Decision's unique identifier. 054 * 055 * @param decision the Decision to add 056 */ 057 protected synchronized void add( final Decision decision ) { 058 m_queue.addLast( decision ); 059 decision.setId( next.getAndIncrement() ); 060 } 061 062 /** 063 * Protected method that returns all pending Decisions in the queue, in order of submission. If no Decisions are pending, this 064 * method returns a zero-length array. 065 * 066 * @return the pending decisions 067 */ 068 protected Decision[] decisions() { 069 return m_queue.toArray( new Decision[0] ); 070 } 071 072 /** 073 * Protected method that removes a Decision from the queue. 074 * 075 * @param decision the decision to remove 076 */ 077 protected synchronized void remove( final Decision decision ) { 078 m_queue.remove( decision ); 079 } 080 081 /** 082 * Returns a Collection representing the current Decisions that pertain to a users's Session. The Decisions are obtained by iterating 083 * through the Session's Principals and selecting those Decisions whose {@link Decision#getActor()} value match. If the session 084 * is not authenticated, this method returns an empty Collection. 085 * 086 * @param session the wiki session 087 * @return the collection of Decisions, which may be empty 088 */ 089 public Collection<Decision> getActorDecisions( final Session session ) { 090 final ArrayList< Decision > decisions = new ArrayList<>(); 091 if( session.isAuthenticated() ) { 092 final Principal[] principals = session.getPrincipals(); 093 final Principal[] rolePrincipals = session.getRoles(); 094 for( final Decision decision : m_queue ) { 095 // Iterate through the Principal set 096 for( final Principal principal : principals ) { 097 if( principal.equals( decision.getActor() ) ) { 098 decisions.add( decision ); 099 } 100 } 101 // Iterate through the Role set 102 for( final Principal principal : rolePrincipals ) { 103 if( principal.equals( decision.getActor() ) ) { 104 decisions.add( decision ); 105 } 106 } 107 } 108 } 109 return decisions; 110 } 111 112 /** 113 * Attempts to complete a Decision by calling {@link Decision#decide(Outcome, Context)}. This will cause the Step immediately following the 114 * Decision (if any) to start. If the decision completes successfully, this method also removes the completed decision from the queue. 115 * 116 * @param decision the Decision for which the Outcome will be supplied 117 * @param outcome the Outcome of the Decision 118 * @throws WikiException if the succeeding Step cannot start for any reason 119 */ 120 public void decide( final Decision decision, final Outcome outcome, final Context context ) throws WikiException { 121 decision.decide( outcome, context ); 122 if ( decision.isCompleted() ) { 123 remove( decision ); 124 } 125 126 WikiEventEmitter.fireWorkflowEvent( decision, WorkflowEvent.DQ_DECIDE ); 127 } 128 129 /** 130 * Reassigns the owner of the Decision to a new owner. Under the covers, this method calls {@link Decision#reassign(Principal)}. 131 * 132 * @param decision the Decision to reassign 133 * @param owner the new owner 134 * @throws WikiException never 135 */ 136 public synchronized void reassign( final Decision decision, final Principal owner ) throws WikiException { 137 if( decision.isReassignable() ) { 138 decision.reassign( owner ); 139 140 WikiEventEmitter.fireWorkflowEvent( decision, WorkflowEvent.DQ_REASSIGN ); 141 return; 142 } 143 throw new IllegalStateException( "Reassignments not allowed for this decision." ); 144 } 145 146}