001/* 002 * Copyright 2016 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.plugins.workflow.support; 017 018import java.util.Collections; 019import java.util.Properties; 020 021import javax.jcr.Repository; 022 023import org.apache.avalon.framework.activity.Disposable; 024import org.apache.avalon.framework.activity.Initializable; 025import org.apache.avalon.framework.component.Component; 026import org.apache.avalon.framework.context.ContextException; 027import org.apache.avalon.framework.context.Contextualizable; 028import org.apache.avalon.framework.service.ServiceException; 029import org.apache.avalon.framework.service.ServiceManager; 030import org.apache.avalon.framework.service.Serviceable; 031import org.apache.cocoon.Constants; 032import org.apache.cocoon.environment.Context; 033import org.apache.excalibur.source.SourceResolver; 034import org.slf4j.Logger; 035import org.slf4j.LoggerFactory; 036 037import org.ametys.core.user.CurrentUserProvider; 038import org.ametys.core.user.UserIdentity; 039import org.ametys.plugins.workflow.AbstractAmetysWorkflow; 040import org.ametys.plugins.workflow.AmetysWorkflowFactory; 041import org.ametys.plugins.workflow.SimpleConfiguration; 042import org.ametys.plugins.workflow.repository.WorkflowAwareAmetysObject; 043import org.ametys.plugins.workflow.store.AmetysObjectWorkflowStore; 044import org.ametys.plugins.workflow.store.GenericWorkflowStore; 045 046import com.opensymphony.workflow.FactoryException; 047import com.opensymphony.workflow.StoreException; 048import com.opensymphony.workflow.TypeResolver; 049import com.opensymphony.workflow.Workflow; 050import com.opensymphony.workflow.WorkflowContext; 051import com.opensymphony.workflow.WorkflowException; 052import com.opensymphony.workflow.loader.WorkflowFactory; 053import com.opensymphony.workflow.spi.WorkflowStore; 054 055/** 056 * Component able to provide several workflow objects. 057 * <ul> 058 * <li>The generic workflow: A standalone workflow which can be used for various 059 * purposes. All entries are stored under a global workflow root node in the JCR 060 * repository. 061 * <li>Dedicated ametys object workflows: A workflow specific to an ametys 062 * object. The workflow is stored under the ametys object node the JCR 063 * repository. 064 * </ul> 065 */ 066public class WorkflowProvider implements Component, Serviceable, Contextualizable, Initializable, Disposable 067{ 068 /** Avalon role. */ 069 public static final String ROLE = WorkflowProvider.class.getName(); 070 071 /** Logger available to subclasses. */ 072 protected static Logger _logger = LoggerFactory.getLogger(WorkflowProvider.class); 073 074 /** Service manager. */ 075 protected ServiceManager _manager; 076 077 /** Current user provider. */ 078 protected CurrentUserProvider _currentUserProvider; 079 080 /** The repository */ 081 protected Repository _repository; 082 083 /** The workflow helper */ 084 protected WorkflowHelper _workflowHelper; 085 086 /** Cocoon context */ 087 protected Context _cocoonContext; 088 089 /** Workflow context */ 090 protected WorkflowContext _workflowContext; 091 092 /** Workflow factory */ 093 protected WorkflowFactory _workflowFactory; 094 095 /** Type resolver */ 096 protected TypeResolver _typeResolver; 097 098 /** Generic workflow instance */ 099 protected GenericWorkflow _genericWorkflow; 100 101 /** The source resolver */ 102 protected SourceResolver _resolver; 103 104 public void contextualize(org.apache.avalon.framework.context.Context ctx) throws ContextException 105 { 106 _cocoonContext = (Context) ctx.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT); 107 } 108 109 public void service(ServiceManager manager) throws ServiceException 110 { 111 _manager = manager; 112 _currentUserProvider = (CurrentUserProvider) _manager.lookup(CurrentUserProvider.ROLE); 113 _repository = (Repository) _manager.lookup("javax.jcr.Repository"); 114 _workflowHelper = (WorkflowHelper) _manager.lookup(WorkflowHelper.ROLE); 115 _resolver = (SourceResolver) _manager.lookup(SourceResolver.ROLE); 116 _workflowFactory = (WorkflowFactory) _manager.lookup(AmetysWorkflowFactory.ROLE); 117 } 118 119 public void initialize() throws Exception 120 { 121 // Standard workflow context 122 _workflowContext = new WorkflowContext() 123 { 124 public String getCaller() 125 { 126 return UserIdentity.userIdentityToString(_currentUserProvider.getUser()); 127 } 128 129 public void setRollbackOnly() 130 { 131 // Transaction not supported 132 } 133 }; 134 135 // Ametys workflow factory 136 try 137 { 138 _workflowFactory.init(new Properties()); 139 _workflowFactory.initDone(); 140 } 141 catch (FactoryException e) 142 { 143 throw new RuntimeException("Unable to init workflow factory", e); 144 } 145 146 // Standard type resolver 147 _typeResolver = new AvalonTypeResolver(_manager); 148 } 149 150 public void dispose() 151 { 152 _manager = null; 153 _currentUserProvider = null; 154 _cocoonContext = null; 155 156 _workflowContext = null; 157 _workflowFactory = null; 158 _typeResolver = null; 159 _genericWorkflow = null; 160 } 161 162 /** 163 * Workflow factory getter 164 * @return The workflow factory 165 */ 166 WorkflowFactory getWorkflowFactory() 167 { 168 return _workflowFactory; 169 } 170 171 /** 172 * Provides the generic workflow 173 * @return The generic workflow 174 */ 175 public Workflow getGenericWorkflow() 176 { 177 if (_genericWorkflow == null) 178 { 179 // Setup the generic workflow 180 GenericWorkflowStore store = _createGenericWorkflowStore(); 181 try 182 { 183 store.init(Collections.EMPTY_MAP); 184 } 185 catch (StoreException e) 186 { 187 throw new RuntimeException("Unable to init workflow store", e); 188 } 189 190 _genericWorkflow = new GenericWorkflow(_workflowHelper, _workflowContext); 191 192 _genericWorkflow.setConfiguration(new SimpleConfiguration(_workflowFactory, store)); 193 _genericWorkflow.setResolver(_typeResolver); 194 } 195 196 return _genericWorkflow; 197 } 198 199 /** 200 * Provide the generic workflow store 201 * @return The generic workflow store 202 */ 203 protected GenericWorkflowStore _createGenericWorkflowStore() 204 { 205 return new GenericWorkflowStore(_repository); 206 } 207 208 /** 209 * The Generic worklfow 210 */ 211 public static class GenericWorkflow extends AbstractAmetysWorkflow 212 { 213 GenericWorkflow(WorkflowHelper workflowHelper, WorkflowContext workflowContext) 214 { 215 super(workflowHelper, workflowContext); 216 } 217 } 218 219 /** 220 * Provide a local workflow to an Ametys object which do not preserve history on workflow complete 221 * Must be used to initialize a workflow that will create an ametys object. 222 * {@link AmetysObjectWorkflow#getAmetysObject()} could then be used to directly retrieves the ametys object. 223 * @return The local workflow 224 */ 225 public AmetysObjectWorkflow getAmetysObjectWorkflow() 226 { 227 return getAmetysObjectWorkflow(null, false); 228 } 229 230 /** 231 * Provide a local workflow to an Ametys object 232 * Must be used to initialize a workflow that will create an ametys object. 233 * {@link AmetysObjectWorkflow#getAmetysObject()} could then be used to directly retrieves the ametys object. 234 * @param preserveHistory true if the history should be preserve when workflow is complete 235 * @return The local workflow 236 */ 237 public AmetysObjectWorkflow getAmetysObjectWorkflow(boolean preserveHistory) 238 { 239 return getAmetysObjectWorkflow(null, preserveHistory); 240 } 241 242 /** 243 * Provide a local workflow to an Ametys object which do not preserve history on workflow complete 244 * @param ametysObject The ametys object (can be null in case of initialization) 245 * @return the local workflow to an Ametys object 246 */ 247 public AmetysObjectWorkflow getAmetysObjectWorkflow(WorkflowAwareAmetysObject ametysObject) 248 { 249 return getAmetysObjectWorkflow(ametysObject, false); 250 } 251 252 /** 253 * Provide a local workflow to an Ametys object 254 * @param ametysObject The ametys object (can be null in case of initialization) 255 * @param preserveHistory true if the history steps should be preserve when workflow is complete 256 * @return the local workflow to an Ametys object 257 */ 258 public AmetysObjectWorkflow getAmetysObjectWorkflow(WorkflowAwareAmetysObject ametysObject, boolean preserveHistory) 259 { 260 AmetysObjectWorkflowStore store = _createAmetysObjectWorkflowStore(ametysObject, preserveHistory); 261 262 try 263 { 264 store.init(Collections.EMPTY_MAP); 265 } 266 catch (StoreException e) 267 { 268 throw new RuntimeException("Unable to init workflow store", e); 269 } 270 271 AmetysObjectWorkflow ametysObjectWorkflow = new AmetysObjectWorkflow(_workflowHelper, _workflowContext, store); 272 ametysObjectWorkflow.setConfiguration(new SimpleConfiguration(_workflowFactory, store)); 273 ametysObjectWorkflow.setResolver(_typeResolver); 274 275 return ametysObjectWorkflow; 276 } 277 278 /** 279 * Provide an ametys object workflow store instance 280 * @param ametysObject The ametys object bound to this store (can be null in case of initialization) 281 * @param preserveHistory true if the history steps should be preserve when workflow is complete 282 * @return the local workflow store of an Ametys object 283 */ 284 protected AmetysObjectWorkflowStore _createAmetysObjectWorkflowStore(WorkflowAwareAmetysObject ametysObject, boolean preserveHistory) 285 { 286 return new AmetysObjectWorkflowStore(_repository, ametysObject, preserveHistory); 287 } 288 289 /** 290 * Local workflow to an ametys object 291 */ 292 public static class AmetysObjectWorkflow extends AbstractAmetysWorkflow 293 { 294 /** The ametys object */ 295 protected AmetysObjectWorkflowStore _workflowStore; 296 297 AmetysObjectWorkflow(WorkflowHelper workflowHelper, WorkflowContext workflowContext, AmetysObjectWorkflowStore store) 298 { 299 super(workflowHelper, workflowContext); 300 _workflowStore = store; 301 } 302 303 /** 304 * Ametys object getter 305 * @param <A> A WorkflowAwareAmetysObject class 306 * @return The ametys object 307 */ 308 @SuppressWarnings("unchecked") 309 public <A extends WorkflowAwareAmetysObject> A getAmetysObject() 310 { 311 return (A) _workflowStore.getAmetysObject(); 312 } 313 314 /** 315 * Delete a workflow instance 316 * @param wId The id of workflow instance 317 * @throws WorkflowException if failed to delete workflow instance 318 */ 319 public void removeWorkflow (long wId) throws WorkflowException 320 { 321 _workflowStore.removeEntry(wId); 322 } 323 324 @Override 325 public String getWorkflowName(long id) 326 { 327 String workflowName = super.getWorkflowName(id); 328 if (workflowName == null) 329 { 330 log.error("Can't get workflow name of " + getAmetysObject().toString()); 331 } 332 return workflowName; 333 } 334 } 335 336 /** 337 * Provides an external workflow 338 * @param workflowStoreRole The component role of the workflow store to use. 339 * @return The external workflow 340 */ 341 public Workflow getExternalWorkflow(String workflowStoreRole) 342 { 343 WorkflowStore workflowStore; 344 try 345 { 346 workflowStore = (WorkflowStore) _manager.lookup(workflowStoreRole); 347 } 348 catch (ServiceException e) 349 { 350 throw new RuntimeException(String.format("Unable to lookup workflow store for role '%s'.", workflowStoreRole), e); 351 } 352 353 ExternalWorkflow workflow = new ExternalWorkflow(_workflowHelper, _workflowContext, workflowStore); 354 355 workflow.setConfiguration(new SimpleConfiguration(_workflowFactory, workflowStore)); 356 workflow.setResolver(_typeResolver); 357 358 return workflow; 359 } 360 361 /** 362 * Provides an external workflow 363 * @param workflowStore An (already initialized) workflow store 364 * @return The external workflow 365 */ 366 public Workflow getExternalWorkflow(WorkflowStore workflowStore) 367 { 368 ExternalWorkflow workflow = new ExternalWorkflow(_workflowHelper, _workflowContext, workflowStore); 369 370 workflow.setConfiguration(new SimpleConfiguration(_workflowFactory, workflowStore)); 371 workflow.setResolver(_typeResolver); 372 373 return workflow; 374 } 375 376 /** 377 * The External worklfow 378 */ 379 public static class ExternalWorkflow extends AbstractAmetysWorkflow 380 { 381 /** The external workflow store */ 382 protected WorkflowStore _store; 383 384 ExternalWorkflow(WorkflowHelper workflowHelper, WorkflowContext workflowContext, WorkflowStore workflowStore) 385 { 386 super(workflowHelper, workflowContext); 387 _store = workflowStore; 388 } 389 } 390}