/* * 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.runtime.manager.impl; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import javax.persistence.EntityManagerFactory; import org.drools.core.builder.conf.impl.DecisionTableConfigurationImpl; import org.drools.core.impl.EnvironmentFactory; import org.jbpm.marshalling.impl.ProcessInstanceResolverStrategy; import org.jbpm.process.core.timer.GlobalSchedulerService; import org.jbpm.runtime.manager.api.SchedulerProvider; import org.jbpm.runtime.manager.impl.mapper.InMemoryMapper; import org.kie.api.KieBase; import org.kie.api.KieServices; import org.kie.api.io.Resource; import org.kie.api.io.ResourceConfiguration; import org.kie.api.io.ResourceType; import org.kie.api.marshalling.ObjectMarshallingStrategy; import org.kie.api.runtime.Environment; import org.kie.api.runtime.EnvironmentName; import org.kie.api.runtime.KieSessionConfiguration; import org.kie.api.runtime.manager.RegisterableItemsFactory; import org.kie.api.task.UserGroupCallback; import org.kie.internal.builder.DecisionTableConfiguration; import org.kie.internal.builder.DecisionTableInputType; import org.kie.internal.builder.KnowledgeBuilder; import org.kie.internal.builder.KnowledgeBuilderError; import org.kie.internal.builder.KnowledgeBuilderFactory; import org.kie.internal.io.ResourceTypeImpl; import org.kie.internal.runtime.conf.ForceEagerActivationOption; import org.kie.internal.runtime.manager.Mapper; import org.kie.internal.runtime.manager.RuntimeEnvironment; /** * The most basic implementation of the <code>RuntimeEnvironment</code> that, at the same time, serves as base * implementation for all extensions. Encapsulates all important configuration that <code>RuntimeManager</code> * requires for execution. * <ul> * <li>EntityManagerFactory - shared for all runtime engine build based on same <code>RuntimeEnvironment</code></li> * <li>Environment - Drools/jBPM environment object - will be cloned for every <code>RuntimeEngine</code></li> * <li>KieSessionConfiguration - will be build passed on defined properties - cloned for every <code>RuntimeEngine</code></li> * <li>KieBase - resulting knowledge base build on given assets or returned if it was preset</li> * <li>RegisterableItemsFactory - factory used to provide listeners and work item handlers</li> * <li>Mapper - mapper used to keep context information</li> * <li>UserGroupCallback - user group callback, if not given null will be returned</li> * <li>GlobalSchedulerService - since this environment implements <code>SchedulerProvider</code> * it allows to get <code>GlobalTimerService</code> if available</li> * </ul> * */ public class SimpleRuntimeEnvironment implements RuntimeEnvironment, SchedulerProvider { protected boolean usePersistence; protected EntityManagerFactory emf; protected Map<String, Object> environmentEntries; protected Environment environment; protected KieSessionConfiguration configuration; protected KieBase kbase; protected KnowledgeBuilder kbuilder; protected RegisterableItemsFactory registerableItemsFactory; protected Mapper mapper; protected UserGroupCallback userGroupCallback; protected GlobalSchedulerService schedulerService; protected ClassLoader classLoader; protected Properties sessionConfigProperties; public SimpleRuntimeEnvironment() { this(new SimpleRegisterableItemsFactory()); } public SimpleRuntimeEnvironment(RegisterableItemsFactory registerableItemsFactory) { this.environment = EnvironmentFactory.newEnvironment(); this.environmentEntries = new HashMap<String, Object>(); this.kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder(); this.registerableItemsFactory = registerableItemsFactory; } public void init() { if (this.mapper == null) { this.mapper = new InMemoryMapper(); } } /** * Adds given asset to knowledge builder to produce KieBase * @param resource asset to be added * @param type type of the asset */ public void addAsset(Resource resource, ResourceType type) { /** * The code below (CSV/XLS) was added because of timelines related to switchyard/fuse. * * However, it is an ugly hack: As soon as is possible, the code below should be removed or refactored. * - an "addAsset(Resource, ResourceType, ResourceConfiguration)" method should be added to this implementation * - or the kbuilder code should be refactored so that there are two ResourceTypes: CSV and XLS * * (refactoring the kbuilder code is probably a better idea.) */ boolean replaced = false; if (resource.getSourcePath() != null ) { String path = resource.getSourcePath(); String typeStr = null; if( path.toLowerCase().endsWith(".csv") ) { typeStr = DecisionTableInputType.CSV.toString(); } else if( path.toLowerCase().endsWith(".xls") ) { typeStr = DecisionTableInputType.XLS.toString(); } if( typeStr != null ) { String worksheetName = null; boolean replaceConfig = true; ResourceConfiguration config = resource.getConfiguration(); if( config != null && config instanceof DecisionTableConfiguration ) { DecisionTableInputType realType = DecisionTableInputType.valueOf(typeStr); if( ((DecisionTableConfiguration) config).getInputType().equals(realType) ) { replaceConfig = false; } else { worksheetName = ((DecisionTableConfiguration) config).getWorksheetName(); } } if( replaceConfig ) { Properties prop = new Properties(); prop.setProperty(ResourceTypeImpl.KIE_RESOURCE_CONF_CLASS, DecisionTableConfigurationImpl.class.getName()); prop.setProperty(DecisionTableConfigurationImpl.DROOLS_DT_TYPE, typeStr); if( worksheetName != null ) { prop.setProperty(DecisionTableConfigurationImpl.DROOLS_DT_WORKSHEET, worksheetName); } ResourceConfiguration conf = ResourceTypeImpl.fromProperties(prop); this.kbuilder.add(resource, type, conf); replaced = true; } } } if( ! replaced ) { this.kbuilder.add(resource, type); } if (this.kbuilder.hasErrors()) { StringBuffer errorMessage = new StringBuffer(); for( KnowledgeBuilderError error : kbuilder.getErrors()) { errorMessage.append(error.getMessage() + ","); } this.kbuilder.undo(); throw new IllegalArgumentException("Cannot add asset: " + errorMessage.toString()); } } /** * Adds element to the drools/jbpm environment - the value must be thread save as it will be shared between all * <code>RuntimeEngine</code> instances * @param name name of the environment entry * @param value value of the environment entry */ public void addToEnvironment(String name, Object value) { this.environment.set(name, value); this.environmentEntries.put(name, value); } /** * Adds configuration property that will be part of <code>KieSessionConfiguration</code> * @param name name of the property * @param value value of the property */ public void addToConfiguration(String name, String value) { if (this.sessionConfigProperties == null) { this.sessionConfigProperties = new Properties(); } this.sessionConfigProperties.setProperty(name, value); } @Override public KieBase getKieBase() { if (this.kbase == null) { this.kbase = kbuilder.newKnowledgeBase(); } return this.kbase; } public Environment getEnvironmentTemplate() { return this.environment; } @Override public Environment getEnvironment() { // this environment is like template always make a new copy when this method is called return copyEnvironment(); } @Override public KieSessionConfiguration getConfiguration() { KieSessionConfiguration config = null; if (this.sessionConfigProperties != null) { config = KieServices.Factory.get().newKieSessionConfiguration(this.sessionConfigProperties, classLoader); } else { config = KieServices.Factory.get().newKieSessionConfiguration(null, classLoader); } // add special option to fire activations marked as eager directly config.setOption(ForceEagerActivationOption.YES); return config; } @Override public boolean usePersistence() { return this.usePersistence; } @Override public RegisterableItemsFactory getRegisterableItemsFactory() { return this.registerableItemsFactory; } @Override public void close() { } protected void addIfPresent(String name, Environment copy) { Object value = this.environment.get(name); if (value != null) { copy.set(name, value); } } protected Environment copyEnvironment() { Environment copy = EnvironmentFactory.newEnvironment(); addIfPresent(EnvironmentName.ENTITY_MANAGER_FACTORY,copy); addIfPresent(EnvironmentName.CALENDARS, copy); addIfPresent(EnvironmentName.DATE_FORMATS, copy); addIfPresent(EnvironmentName.GLOBALS, copy); addIfPresent(EnvironmentName.OBJECT_MARSHALLING_STRATEGIES, copy); addIfPresent(EnvironmentName.PERSISTENCE_CONTEXT_MANAGER, copy); addIfPresent(EnvironmentName.TASK_PERSISTENCE_CONTEXT_MANAGER, copy); addIfPresent(EnvironmentName.TRANSACTION_MANAGER, copy); addIfPresent(EnvironmentName.TRANSACTION_SYNCHRONIZATION_REGISTRY, copy); addIfPresent(EnvironmentName.TRANSACTION, copy); addIfPresent(EnvironmentName.USE_LOCAL_TRANSACTIONS, copy); addIfPresent(EnvironmentName.USE_PESSIMISTIC_LOCKING, copy); addIfPresent(EnvironmentName.EXEC_ERROR_MANAGER, copy); if (usePersistence()) { ObjectMarshallingStrategy[] strategies = (ObjectMarshallingStrategy[]) copy.get(EnvironmentName.OBJECT_MARSHALLING_STRATEGIES); List<ObjectMarshallingStrategy> listStrategies = new ArrayList<ObjectMarshallingStrategy>(Arrays.asList(strategies)); listStrategies.add(0, new ProcessInstanceResolverStrategy()); strategies = new ObjectMarshallingStrategy[listStrategies.size()]; copy.set(EnvironmentName.OBJECT_MARSHALLING_STRATEGIES, listStrategies.toArray(strategies)); } // copy if present in environment template which in general should not be used // unless with some framework support to make EM thread safe - like spring addIfPresent(EnvironmentName.APP_SCOPED_ENTITY_MANAGER, copy); addIfPresent(EnvironmentName.CMD_SCOPED_ENTITY_MANAGER, copy); addIfPresent("IS_JTA_TRANSACTION", copy); addIfPresent("IS_TIMER_CMT", copy); addIfPresent("IS_SHARED_ENTITY_MANAGER", copy); addIfPresent("TRANSACTION_LOCK_ENABLED", copy); addIfPresent("IdentityProvider", copy); addIfPresent("jbpm.business.calendar", copy); // handle for custom environment entries that might be required by non engine use cases if (!environmentEntries.isEmpty()) { for (Entry<String, Object> entry : environmentEntries.entrySet()) { // don't override if (copy.get(entry.getKey()) != null) { continue; } copy.set(entry.getKey(), entry.getValue()); } } return copy; } @Override public Mapper getMapper() { return this.mapper; } @Override public UserGroupCallback getUserGroupCallback() { return this.userGroupCallback; } public void setUserGroupCallback(UserGroupCallback userGroupCallback) { this.userGroupCallback = userGroupCallback; } public Properties getSessionConfigProperties() { return sessionConfigProperties; } public void setSessionConfigProperties(Properties sessionConfigProperties) { this.sessionConfigProperties = sessionConfigProperties; } public void setUsePersistence(boolean usePersistence) { this.usePersistence = usePersistence; } public void setKieBase(KieBase kbase) { this.kbase = kbase; } public void setMapper(Mapper mapper) { this.mapper = mapper; } @Override public GlobalSchedulerService getSchedulerService() { return this.schedulerService; } public void setSchedulerService(GlobalSchedulerService schedulerService) { this.schedulerService = schedulerService; } public void setRegisterableItemsFactory(RegisterableItemsFactory registerableItemsFactory) { this.registerableItemsFactory = registerableItemsFactory; } public EntityManagerFactory getEmf() { return emf; } public void setEmf(EntityManagerFactory emf) { this.emf = emf; } public ClassLoader getClassLoader() { return classLoader; } public void setClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } }