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