/* * Copyright 2013 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jbpm.services.task; import java.lang.reflect.Constructor; import java.util.HashSet; import java.util.Set; import java.util.TreeSet; import javax.persistence.EntityManagerFactory; import org.drools.core.impl.EnvironmentFactory; import org.drools.core.runtime.ChainableRunner; import org.jbpm.services.task.assignment.AssignmentServiceProvider; import org.jbpm.services.task.assignment.impl.AssignmentTaskEventListener; import org.jbpm.services.task.commands.TaskCommandExecutorImpl; import org.jbpm.services.task.events.TaskEventSupport; import org.jbpm.services.task.identity.DefaultUserInfo; import org.jbpm.services.task.identity.MvelUserGroupCallbackImpl; import org.jbpm.services.task.impl.TaskDeadlinesServiceImpl; import org.jbpm.services.task.impl.command.CommandBasedTaskService; import org.kie.api.runtime.Environment; import org.kie.api.runtime.EnvironmentName; import org.kie.api.task.TaskLifeCycleEventListener; import org.kie.api.task.TaskService; import org.kie.api.task.UserGroupCallback; import org.kie.internal.task.api.EventService; import org.kie.internal.task.api.UserInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Task service configurator that provides fluent API approach to building <code>TaskService</code> * instances. Most of the attributes have their defaults but there is one that must be explicitly set * <ul> * <li>entityManagerFactory</li> * </ul> * Important to notice is defaults for: * <ul> * <li>userInfo - DefaultUserInfo by default</li> * <li>userGroupCallback - uses MvelUserGroupCallbackImpl by default</li> * </ul> * * @see DefaultUserInfo * @see MvelUserGroupCallbackImpl */ public class HumanTaskConfigurator { private static final Logger logger = LoggerFactory.getLogger(HumanTaskConfigurator.class); private static final String DEFAULT_INTERCEPTOR = "org.jbpm.services.task.persistence.TaskTransactionInterceptor"; private static final String TX_LOCK_INTERCEPTOR = "org.drools.persistence.jta.TransactionLockInterceptor"; private static final String OPTIMISTIC_LOCK_INTERCEPTOR = "org.drools.persistence.jpa.OptimisticLockRetryInterceptor"; private static final String ERROR_HANDLING_INTERCEPTOR = "org.jbpm.runtime.manager.impl.error.ExecutionErrorHandlerInterceptor"; private TaskService service; private TaskCommandExecutorImpl commandExecutor; private Environment environment = EnvironmentFactory.newEnvironment(); private UserGroupCallback userGroupCallback; private UserInfo userInfo; private Set<PriorityInterceptor> interceptors = new TreeSet<PriorityInterceptor>(); private Set<TaskLifeCycleEventListener> listeners = new HashSet<TaskLifeCycleEventListener>(); public HumanTaskConfigurator interceptor(int priority, ChainableRunner interceptor ) { if (interceptor == null) { return this; } this.interceptors.add(new PriorityInterceptor(priority, interceptor)); return this; } public HumanTaskConfigurator listener(TaskLifeCycleEventListener listener) { if (listener == null) { return this; } this.listeners.add(listener); return this; } public HumanTaskConfigurator environment(Environment environment) { if (environment == null) { return this; } this.environment = environment; return this; } public HumanTaskConfigurator entityManagerFactory(EntityManagerFactory emf) { if (emf == null) { return this; } environment.set(EnvironmentName.ENTITY_MANAGER_FACTORY, emf); return this; } public HumanTaskConfigurator userInfo(UserInfo userInfo) { if (userInfo == null) { return this; } this.userInfo = userInfo; return this; } public HumanTaskConfigurator userGroupCallback(UserGroupCallback userGroupCallback) { if (userGroupCallback == null) { return this; } this.userGroupCallback = userGroupCallback; return this; } @SuppressWarnings("unchecked") public TaskService getTaskService() { if (service == null) { TaskEventSupport taskEventSupport = new TaskEventSupport(); this.commandExecutor = new TaskCommandExecutorImpl(this.environment, taskEventSupport); if (userGroupCallback == null) { userGroupCallback = new MvelUserGroupCallbackImpl(true); } environment.set(EnvironmentName.TASK_USER_GROUP_CALLBACK, userGroupCallback); if (userInfo == null) { userInfo = new DefaultUserInfo(true); } environment.set(EnvironmentName.TASK_USER_INFO, userInfo); addDefaultInterceptor(); addTransactionLockInterceptor(); addOptimisticLockInterceptor(); addErrorHandlingInterceptor(); for (PriorityInterceptor pInterceptor : interceptors) { this.commandExecutor.addInterceptor(pInterceptor.getInterceptor()); } service = new CommandBasedTaskService(this.commandExecutor, taskEventSupport); // register listeners for (TaskLifeCycleEventListener listener : listeners) { ((EventService<TaskLifeCycleEventListener>) service).registerTaskEventListener(listener); } if (AssignmentServiceProvider.get().isEnabled()) { ((EventService<TaskLifeCycleEventListener>) service).registerTaskEventListener(new AssignmentTaskEventListener()); } // initialize deadline service with command executor for processing if (TaskDeadlinesServiceImpl.getInstance() == null) { TaskDeadlinesServiceImpl.initialize(commandExecutor); } } return service; } @SuppressWarnings("unchecked") protected void addDefaultInterceptor() { // add default interceptor if present try { Class<ChainableRunner> defaultInterceptorClass = (Class<ChainableRunner>) Class.forName(DEFAULT_INTERCEPTOR); Constructor<ChainableRunner> constructor = defaultInterceptorClass.getConstructor(new Class[] {Environment.class}); ChainableRunner defaultInterceptor = constructor.newInstance(this.environment); interceptor(5, defaultInterceptor); } catch (Exception e) { logger.warn("No default interceptor found of type {} might be mssing jbpm-human-task-jpa module on classpath (error {}", DEFAULT_INTERCEPTOR, e.getMessage(), e); } } @SuppressWarnings("unchecked") protected void addTransactionLockInterceptor() { // add default interceptor if present try { Class<ChainableRunner> defaultInterceptorClass = (Class<ChainableRunner>) Class.forName(TX_LOCK_INTERCEPTOR); Constructor<ChainableRunner> constructor = defaultInterceptorClass.getConstructor(new Class[] {Environment.class, String.class}); ChainableRunner defaultInterceptor = constructor.newInstance(this.environment, "task-service-tx-unlock"); interceptor(6, defaultInterceptor); } catch (Exception e) { logger.warn("No tx lock interceptor found of type {} might be mssing drools-persistence-jpa module on classpath (error {}", TX_LOCK_INTERCEPTOR, e.getMessage(), e); } } @SuppressWarnings("unchecked") protected void addOptimisticLockInterceptor() { // add default interceptor if present try { Class<ChainableRunner> defaultInterceptorClass = (Class<ChainableRunner>) Class.forName(OPTIMISTIC_LOCK_INTERCEPTOR); Constructor<ChainableRunner> constructor = defaultInterceptorClass.getConstructor(new Class[] {}); ChainableRunner defaultInterceptor = constructor.newInstance(); interceptor(7, defaultInterceptor); } catch (Exception e) { logger.warn("No optimistic lock interceptor found of type {} might be mssing drools-persistence-jpa module on classpath (error {}", OPTIMISTIC_LOCK_INTERCEPTOR, e.getMessage(), e); } } @SuppressWarnings("unchecked") protected void addErrorHandlingInterceptor() { // add error handling interceptor if present try { Class<ChainableRunner> defaultInterceptorClass = (Class<ChainableRunner>) Class.forName(ERROR_HANDLING_INTERCEPTOR); Constructor<ChainableRunner> constructor = defaultInterceptorClass.getConstructor(new Class[] {Environment.class}); ChainableRunner defaultInterceptor = constructor.newInstance(this.environment); interceptor(8, defaultInterceptor); } catch (Exception e) { logger.debug("No error handling interceptor found of type {} might be missing jbpm-runtime-manager module on classpath (error {}", ERROR_HANDLING_INTERCEPTOR, e.getMessage(), e); } } private static class PriorityInterceptor implements Comparable<PriorityInterceptor> { private Integer priority; private ChainableRunner interceptor; PriorityInterceptor(Integer priority, ChainableRunner interceptor) { this.priority = priority; this.interceptor = interceptor; } public Integer getPriority() { return priority; } public ChainableRunner getInterceptor() { return interceptor; } @Override public int compareTo(PriorityInterceptor other) { return this.getPriority().compareTo(other.getPriority()); } @Override public String toString() { return "PriorityInterceptor [priority=" + priority + ", interceptor=" + interceptor + "]"; } } }