/*
* 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;
}
}