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.SimpleConfiguration; 041import org.ametys.plugins.workflow.XmlWorkflowFactory; 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 } 117 118 public void initialize() throws Exception 119 { 120 // Standard workflow context 121 _workflowContext = new WorkflowContext() 122 { 123 public String getCaller() 124 { 125 return UserIdentity.userIdentityToString(_currentUserProvider.getUser()); 126 } 127 128 public void setRollbackOnly() 129 { 130 // Transaction not supported 131 } 132 }; 133 134 // Standard workflow factory 135 _workflowFactory = new XmlWorkflowFactory(_cocoonContext.getRealPath("/WEB-INF/param")); 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 273 ametysObjectWorkflow.setConfiguration(new SimpleConfiguration(_workflowFactory, store)); 274 ametysObjectWorkflow.setResolver(_typeResolver); 275 276 return ametysObjectWorkflow; 277 } 278 279 /** 280 * Provide an ametys object workflow store instance 281 * @param ametysObject The ametys object bound to this store (can be null in case of initialization) 282 * @param preserveHistory true if the history steps should be preserve when workflow is complete 283 * @return the local workflow store of an Ametys object 284 */ 285 protected AmetysObjectWorkflowStore _createAmetysObjectWorkflowStore(WorkflowAwareAmetysObject ametysObject, boolean preserveHistory) 286 { 287 return new AmetysObjectWorkflowStore(_repository, ametysObject, preserveHistory); 288 } 289 290 /** 291 * Local workflow to an ametys object 292 */ 293 public static class AmetysObjectWorkflow extends AbstractAmetysWorkflow 294 { 295 /** The ametys object */ 296 protected AmetysObjectWorkflowStore _workflowStore; 297 298 AmetysObjectWorkflow(WorkflowHelper workflowHelper, WorkflowContext workflowContext, AmetysObjectWorkflowStore store) 299 { 300 super(workflowHelper, workflowContext); 301 _workflowStore = store; 302 } 303 304 /** 305 * Ametys object getter 306 * @param <A> A WorkflowAwareAmetysObject class 307 * @return The ametys object 308 */ 309 @SuppressWarnings("unchecked") 310 public <A extends WorkflowAwareAmetysObject> A getAmetysObject() 311 { 312 return (A) _workflowStore.getAmetysObject(); 313 } 314 315 /** 316 * Delete a workflow instance 317 * @param wId The id of workflow instance 318 * @throws WorkflowException if failed to delete workflow instance 319 */ 320 public void removeWorkflow (long wId) throws WorkflowException 321 { 322 _workflowStore.removeEntry(wId); 323 } 324 } 325 326 /** 327 * Provides an external workflow 328 * @param workflowStoreRole The component role of the workflow store to use. 329 * @return The external workflow 330 */ 331 public Workflow getExternalWorkflow(String workflowStoreRole) 332 { 333 WorkflowStore workflowStore; 334 try 335 { 336 workflowStore = (WorkflowStore) _manager.lookup(workflowStoreRole); 337 } 338 catch (ServiceException e) 339 { 340 throw new RuntimeException(String.format("Unable to lookup workflow store for role '%s'.", workflowStoreRole), e); 341 } 342 343 ExternalWorkflow workflow = new ExternalWorkflow(_workflowHelper, _workflowContext, workflowStore); 344 345 workflow.setConfiguration(new SimpleConfiguration(_workflowFactory, workflowStore)); 346 workflow.setResolver(_typeResolver); 347 348 return workflow; 349 } 350 351 /** 352 * Provides an external workflow 353 * @param workflowStore An (already initialized) workflow store 354 * @return The external workflow 355 */ 356 public Workflow getExternalWorkflow(WorkflowStore workflowStore) 357 { 358 ExternalWorkflow workflow = new ExternalWorkflow(_workflowHelper, _workflowContext, workflowStore); 359 360 workflow.setConfiguration(new SimpleConfiguration(_workflowFactory, workflowStore)); 361 workflow.setResolver(_typeResolver); 362 363 return workflow; 364 } 365 366 /** 367 * The External worklfow 368 */ 369 public static class ExternalWorkflow extends AbstractAmetysWorkflow 370 { 371 /** The external workflow store */ 372 protected WorkflowStore _store; 373 374 ExternalWorkflow(WorkflowHelper workflowHelper, WorkflowContext workflowContext, WorkflowStore workflowStore) 375 { 376 super(workflowHelper, workflowContext); 377 _store = workflowStore; 378 } 379 } 380}