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