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