    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}