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 }