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     */
019    package org.apache.wiki.workflow;
020    
021    import java.io.Serializable;
022    import java.security.Principal;
023    import java.util.*;
024    
025    import org.apache.wiki.api.exceptions.WikiException;
026    
027    /**
028     * Abstact superclass that provides a complete implementation of most
029     * Step methods; subclasses need only implement {@link #execute()} and
030     * {@link #getActor()}.
031     *
032     * @since 2.5
033     */
034    public abstract class AbstractStep implements Step
035    {
036    
037        private static final long serialVersionUID = 8635678679349653768L;
038    
039        /** Timestamp of when the step started. */
040        private Date m_start;
041    
042        /** Timestamp of when the step ended. */
043        private Date m_end;
044    
045        private final String m_key;
046    
047        private boolean m_completed;
048    
049        private final Map<Outcome, Step> m_successors;
050    
051        private Workflow m_workflow;
052    
053        private Outcome m_outcome;
054    
055        private final List<String> m_errors;
056    
057        private boolean m_started;
058    
059        /**
060         * Protected constructor that creates a new Step with a specified message key.
061         * After construction, the protected method {@link #setWorkflow(Workflow)} should be
062         * called.
063         *
064         * @param messageKey
065         *            the Step's message key, such as
066         *            <code>decision.editPageApproval</code>. By convention, the
067         *            message prefix should be a lower-case version of the Step's
068         *            type, plus a period (<em>e.g.</em>, <code>task.</code>
069         *            and <code>decision.</code>).
070         */
071        protected AbstractStep( String messageKey )
072        {
073            m_started = false;
074            m_start = Workflow.TIME_NOT_SET;
075            m_completed = false;
076            m_end = Workflow.TIME_NOT_SET;
077            m_errors = new ArrayList<String>();
078            m_outcome = Outcome.STEP_CONTINUE;
079            m_key = messageKey;
080            m_successors = new LinkedHashMap<Outcome, Step>();
081        }
082    
083        /**
084         * Constructs a new Step belonging to a specified Workflow and having a
085         * specified message key.
086         *
087         * @param workflow
088         *            the workflow the Step belongs to
089         * @param messageKey
090         *            the Step's message key, such as
091         *            <code>decision.editPageApproval</code>. By convention, the
092         *            message prefix should be a lower-case version of the Step's
093         *            type, plus a period (<em>e.g.</em>, <code>task.</code>
094         *            and <code>decision.</code>).
095         */
096        public AbstractStep( Workflow workflow, String messageKey )
097        {
098            this( messageKey );
099            setWorkflow( workflow );
100        }
101    
102        /**
103         * {@inheritDoc}
104         */
105        public final void addSuccessor(Outcome outcome, Step step)
106        {
107            m_successors.put( outcome, step );
108        }
109    
110        /**
111         * {@inheritDoc}
112         */
113        public final Collection getAvailableOutcomes()
114        {
115            Set<Outcome> outcomes = m_successors.keySet();
116            return Collections.unmodifiableCollection( outcomes );
117        }
118    
119        /**
120         * {@inheritDoc}
121         */
122        public final List getErrors()
123        {
124            return Collections.unmodifiableList( m_errors );
125        }
126    
127        /**
128         * {@inheritDoc}
129         */
130        public abstract Outcome execute() throws WikiException;
131    
132        /**
133         * {@inheritDoc}
134         */
135        public abstract Principal getActor();
136    
137        /**
138         * {@inheritDoc}
139         */
140        public final Date getEndTime()
141        {
142            return m_end;
143        }
144    
145        /**
146         * {@inheritDoc}
147         */
148        public final Serializable[] getMessageArguments()
149        {
150            if ( m_workflow == null )
151            {
152                return new Serializable[0];
153            }
154            return m_workflow.getMessageArguments();
155        }
156    
157        /**
158         * {@inheritDoc}
159         */
160        public final String getMessageKey()
161        {
162            return m_key;
163        }
164    
165        /**
166         * {@inheritDoc}
167         */
168        public final synchronized Outcome getOutcome()
169        {
170            return m_outcome;
171        }
172    
173        /**
174         * {@inheritDoc}
175         */
176        public Principal getOwner()
177        {
178            if ( m_workflow == null )
179            {
180                return null;
181            }
182            return m_workflow.getOwner();
183        }
184    
185        /**
186         * {@inheritDoc}
187         */
188        public final Date getStartTime()
189        {
190            return m_start;
191        }
192    
193        /**
194         * {@inheritDoc}
195         */
196        public final synchronized Workflow getWorkflow()
197        {
198            return m_workflow;
199        }
200    
201        /**
202         * {@inheritDoc}
203         */
204        public final boolean isCompleted()
205        {
206            return m_completed;
207        }
208    
209        /**
210         * {@inheritDoc}
211         */
212        public final boolean isStarted()
213        {
214            return m_started;
215        }
216    
217        /**
218         * {@inheritDoc}
219         */
220        public final synchronized void setOutcome(Outcome outcome)
221        {
222            // Is this an allowed Outcome?
223            if ( !m_successors.containsKey( outcome ) )
224            {
225                if ( !Outcome.STEP_CONTINUE.equals( outcome ) &&
226                     !Outcome.STEP_ABORT.equals( outcome ) )
227                {
228                     throw new IllegalArgumentException( "Outcome " + outcome.getMessageKey() + " is not supported for this Step." );
229                }
230            }
231    
232            // Is this a "completion" outcome?
233            if ( outcome.isCompletion() )
234            {
235                if ( m_completed )
236                {
237                    throw new IllegalStateException( "Step has already been marked complete; cannot set again." );
238                }
239                m_completed = true;
240                m_end = new Date( System.currentTimeMillis() );
241            }
242            m_outcome = outcome;
243        }
244    
245        /**
246         * {@inheritDoc}
247         */
248        public final synchronized void start() throws WikiException
249        {
250            if ( m_started )
251            {
252                throw new IllegalStateException( "Step already started." );
253            }
254            m_started = true;
255            m_start = new Date( System.currentTimeMillis() );
256        }
257    
258        /**
259         * {@inheritDoc}
260         */
261        public final Step getSuccessor( Outcome outcome )
262        {
263            return m_successors.get( outcome );
264        }
265    
266        // --------------------------Helper methods--------------------------
267    
268        /**
269         * Protected method that sets the parent Workflow post-construction.
270         * @param workflow the parent workflow to set
271         */
272        protected final synchronized void setWorkflow( Workflow workflow )
273        {
274            m_workflow = workflow;
275        }
276    
277        /**
278         * Protected helper method that adds a String representing an error message
279         * to the Step's cached errors list.
280         *
281         * @param message
282         *            the error message
283         */
284        protected final synchronized void addError( String message )
285        {
286            m_errors.add( message );
287        }
288    
289    }