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 }