001: /*
002: JSPWiki - a JSP-based WikiWiki clone.
003:
004: Copyright (C) 2001-2007 Janne Jalkanen (Janne.Jalkanen@iki.fi)
005:
006: This program is free software; you can redistribute it and/or modify
007: it under the terms of the GNU Lesser General Public License as published by
008: the Free Software Foundation; either version 2.1 of the License, or
009: (at your option) any later version.
010:
011: This program is distributed in the hope that it will be useful,
012: but WITHOUT ANY WARRANTY; without even the implied warranty of
013: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: GNU Lesser General Public License for more details.
015:
016: You should have received a copy of the GNU Lesser General Public License
017: along with this program; if not, write to the Free Software
018: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: */
020: package com.ecyrd.jspwiki.workflow;
021:
022: import java.security.Principal;
023: import java.util.*;
024:
025: import com.ecyrd.jspwiki.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: * @author Andrew Jaquith
033: * @since 2.5
034: */
035: public abstract class AbstractStep implements Step {
036:
037: /** Timestamp of when the step started. */
038: private Date m_start;
039:
040: /** Timestamp of when the step ended. */
041: private Date m_end;
042:
043: private final String m_key;
044:
045: private boolean m_completed;
046:
047: private final Map m_successors;
048:
049: private Workflow m_workflow;
050:
051: private Outcome m_outcome;
052:
053: private final List m_errors;
054:
055: private boolean m_started;
056:
057: /**
058: * Protected constructor that creates a new Step with a specified message key.
059: * After construction, the protected method {@link #setWorkflow(Workflow)} should be
060: * called.
061: *
062: * @param messageKey
063: * the Step's message key, such as
064: * <code>decision.editPageApproval</code>. By convention, the
065: * message prefix should be a lower-case version of the Step's
066: * type, plus a period (<em>e.g.</em>, <code>task.</code>
067: * and <code>decision.</code>).
068: */
069: protected AbstractStep(String messageKey) {
070: m_started = false;
071: m_start = Workflow.TIME_NOT_SET;
072: m_completed = false;
073: m_end = Workflow.TIME_NOT_SET;
074: m_errors = new ArrayList();
075: m_outcome = Outcome.STEP_CONTINUE;
076: m_key = messageKey;
077: m_successors = new LinkedHashMap();
078: }
079:
080: /**
081: * Constructs a new Step belonging to a specified Workflow and having a
082: * specified message key.
083: *
084: * @param workflow
085: * the workflow the Step belongs to
086: * @param messageKey
087: * the Step's message key, such as
088: * <code>decision.editPageApproval</code>. By convention, the
089: * message prefix should be a lower-case version of the Step's
090: * type, plus a period (<em>e.g.</em>, <code>task.</code>
091: * and <code>decision.</code>).
092: */
093: public AbstractStep(Workflow workflow, String messageKey) {
094: this (messageKey);
095: setWorkflow(workflow);
096: }
097:
098: /**
099: * {@inheritDoc}
100: */
101: public final void addSuccessor(Outcome outcome, Step step) {
102: m_successors.put(outcome, step);
103: }
104:
105: /**
106: * {@inheritDoc}
107: */
108: public final Collection getAvailableOutcomes() {
109: Set outcomes = m_successors.keySet();
110: return Collections.unmodifiableCollection(outcomes);
111: }
112:
113: /**
114: * {@inheritDoc}
115: */
116: public final List getErrors() {
117: return Collections.unmodifiableList(m_errors);
118: }
119:
120: /**
121: * {@inheritDoc}
122: */
123: public abstract Outcome execute() throws WikiException;
124:
125: /**
126: * {@inheritDoc}
127: */
128: public abstract Principal getActor();
129:
130: /**
131: * {@inheritDoc}
132: */
133: public final Date getEndTime() {
134: return m_end;
135: }
136:
137: /**
138: * {@inheritDoc}
139: */
140: public final Object[] getMessageArguments() {
141: if (m_workflow == null) {
142: return new Object[0];
143: }
144: return m_workflow.getMessageArguments();
145: }
146:
147: /**
148: * {@inheritDoc}
149: */
150: public final String getMessageKey() {
151: return m_key;
152: }
153:
154: /**
155: * {@inheritDoc}
156: */
157: public final synchronized Outcome getOutcome() {
158: return m_outcome;
159: }
160:
161: /**
162: * {@inheritDoc}
163: */
164: public Principal getOwner() {
165: if (m_workflow == null) {
166: return null;
167: }
168: return m_workflow.getOwner();
169: }
170:
171: /**
172: * {@inheritDoc}
173: */
174: public final Date getStartTime() {
175: return m_start;
176: }
177:
178: /**
179: * {@inheritDoc}
180: */
181: public final synchronized Workflow getWorkflow() {
182: return m_workflow;
183: }
184:
185: /**
186: * {@inheritDoc}
187: */
188: public final boolean isCompleted() {
189: return m_completed;
190: }
191:
192: /**
193: * {@inheritDoc}
194: */
195: public final boolean isStarted() {
196: return m_started;
197: }
198:
199: /**
200: * {@inheritDoc}
201: */
202: public final synchronized void setOutcome(Outcome outcome) {
203: // Is this an allowed Outcome?
204: if (!m_successors.containsKey(outcome)) {
205: if (!Outcome.STEP_CONTINUE.equals(outcome)
206: && !Outcome.STEP_ABORT.equals(outcome)) {
207: throw new IllegalArgumentException("Outcome "
208: + outcome.getMessageKey()
209: + " is not supported for this Step.");
210: }
211: }
212:
213: // Is this a "completion" outcome?
214: if (outcome.isCompletion()) {
215: if (m_completed) {
216: throw new IllegalStateException(
217: "Step has already been marked complete; cannot set again.");
218: }
219: m_completed = true;
220: m_end = new Date(System.currentTimeMillis());
221: }
222: m_outcome = outcome;
223: }
224:
225: /**
226: * {@inheritDoc}
227: */
228: public final synchronized void start() throws WikiException {
229: if (m_started) {
230: throw new IllegalStateException("Step already started.");
231: }
232: m_started = true;
233: m_start = new Date(System.currentTimeMillis());
234: }
235:
236: /**
237: * {@inheritDoc}
238: */
239: public final Step getSuccessor(Outcome outcome) {
240: return (Step) m_successors.get(outcome);
241: }
242:
243: // --------------------------Helper methods--------------------------
244:
245: /**
246: * Protected method that sets the parent Workflow post-construction.
247: * @param workflow the parent workflow to set
248: */
249: protected final synchronized void setWorkflow(Workflow workflow) {
250: m_workflow = workflow;
251: }
252:
253: /**
254: * Protected helper method that adds a String representing an error message
255: * to the Step's cached errors list.
256: *
257: * @param message
258: * the error message
259: */
260: protected final synchronized void addError(String message) {
261: m_errors.add(message);
262: }
263:
264: }
|