/**
* @author iago.corbal
*/
package se.cambio.cds.controller.execution;
import org.apache.log4j.Logger;
import org.drools.KnowledgeBase;
import org.drools.KnowledgeBaseFactory;
import org.drools.definition.KnowledgePackage;
import org.drools.runtime.StatelessKnowledgeSession;
import org.openehr.rm.datatypes.quantity.datetime.DvDateTime;
import se.cambio.cds.model.instance.ElementInstance;
import se.cambio.cds.util.ExecutionLogger;
import se.cambio.cds.util.RuleExecutionWMLogger;
import se.cambio.cm.model.guide.dto.GuideDTO;
import se.cambio.openehr.util.ExceptionHandler;
import se.cambio.openehr.util.exceptions.InternalErrorException;
import se.cambio.openehr.util.misc.CDSConfigurationParametersManager;
import se.cambio.openehr.util.misc.DataValueGenerator;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.util.*;
public class DroolsExecutionManager {
public Map<String, KnowledgeBase> _knowledgeBaseCache = null;
private static DroolsExecutionManager _instance = null;
private static final short MAX_KNOWLEDGE_BASE_CACHE = 10;
private boolean _useCache = true;
private ExecutionLogger _logger = null;
private Long _timeOutInMillis = null;
private DroolsExecutionManager(){
_knowledgeBaseCache = Collections.synchronizedMap(new LinkedHashMap <String, KnowledgeBase>());
}
public static void executeGuides(
Collection<GuideDTO> guideDTOs,
Calendar date,
Collection<Object> workingMemoryObjects,
ExecutionLogger executionLogger)
throws InternalErrorException{
KnowledgeBase kb = null;
if (getDelegate()._useCache){
kb = getKnowledgeBase(guideDTOs);
}else{
kb = generateKnowledgeBase(guideDTOs);
}
Collection<String> guideIds = new ArrayList<String>();
for(GuideDTO guideDTO: guideDTOs){
guideIds.add(guideDTO.getId());
}
executeGuides(guideIds, kb, date, workingMemoryObjects, executionLogger);
}
private static void executeGuides(
Collection<String> guideIds,
KnowledgeBase knowledgeBase,
Calendar date,
Collection<Object> workingMemoryObjects,
ExecutionLogger executionLogger)
throws InternalErrorException{
try{
final StatelessKnowledgeSession session = knowledgeBase.newStatelessKnowledgeSession();
final RuleExecutionWMLogger ruleExecutionWMLogger = new RuleExecutionWMLogger();
session.addEventListener(ruleExecutionWMLogger);
if (date==null){
date = Calendar.getInstance();
}
final DvDateTime currentDateTime = DataValueGenerator.toDvDateTime(date);
session.setGlobal("$currentDateTime", currentDateTime);
getDelegate()._logger = executionLogger;
session.setGlobal("$executionLogger", executionLogger);
session.setGlobal("$bindingMap", new HashMap<ElementInstance, Map<String, Boolean>>());
session.setGlobal("$execute", true);
int initSalience = 0;
for(String guideId: guideIds) {
session.setGlobal(getGuideSalienceId(guideId), initSalience);
initSalience = initSalience+1000;
}
session.execute(workingMemoryObjects);
executionLogger.setFiredRules(ruleExecutionWMLogger.getFiredRules());
}catch(Exception e){
e.printStackTrace();
throw new InternalErrorException(e);
}
}
public static void cancelCurrentExecution(){
if (getDelegate()._logger!=null){
//TODO Cancel current execution is done through the logger... should change this behaviour
getDelegate()._logger.cancelExecution();
}
}
private static KnowledgeBase getKnowledgeBase(Collection<GuideDTO> guideDTOs)
throws InternalErrorException{
if (guideDTOs==null || guideDTOs.isEmpty()){
return null;
}
String guideIdsId = getGuideIdsId(guideDTOs);
KnowledgeBase kb = getDelegate()._knowledgeBaseCache.get(guideIdsId);
if (kb==null){
kb = DroolsExecutionManager.generateKnowledgeBase(guideDTOs);
getDelegate()._knowledgeBaseCache.put(guideIdsId, kb);
if (getDelegate()._knowledgeBaseCache.size()>MAX_KNOWLEDGE_BASE_CACHE){
//Remove oldest KB in cache
String oldestGuideIdsId = getDelegate()._knowledgeBaseCache.keySet().iterator().next();
getDelegate()._knowledgeBaseCache.remove(oldestGuideIdsId);
Logger.getLogger(DroolsExecutionManager.class).warn("KnowledgeBase cache full. Removing oldest KB: " + guideIdsId);
}
}
return kb;
}
public static void setUseCache(boolean useCache){
Logger.getLogger(DroolsExecutionManager.class).warn("USE-CACHE on cds engine changed to '" + useCache + "'");
getDelegate()._useCache = useCache;
}
public static void clearCache(){
getDelegate()._knowledgeBaseCache.clear();
}
private static String getGuideIdsId(Collection<GuideDTO> guideDTOs) {
List<String> guideIdsIdList = new ArrayList<String>();
for (GuideDTO guideDTO : guideDTOs) {
guideIdsIdList.add(guideDTO.getId());
}
Collections.sort(guideIdsIdList);
StringBuffer guideIdsIdSB = new StringBuffer();
for (String guideId : guideIdsIdList) {
guideIdsIdSB.append(guideId);
}
return guideIdsIdSB.toString();
}
private static KnowledgeBase generateKnowledgeBase(Collection<GuideDTO> guideDTOs) {
ArrayList<KnowledgePackage> knowledgePackages = new ArrayList<KnowledgePackage>();
for (GuideDTO guideDTO : guideDTOs) {
if (guideDTO.getCompiledGuide()==null){
Logger.getLogger(DroolsExecutionManager.class).warn("Guide '" + guideDTO.getId() + "' is not compiled.");
};
KnowledgePackage knowledgePackage =
DroolsExecutionManager.getKnowledgePackage(guideDTO.getCompiledGuide());
if (knowledgePackage!=null){
knowledgePackages.add(knowledgePackage);
}
}
final KnowledgeBase knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase();
if (!knowledgePackages.isEmpty()){
knowledgeBase.addKnowledgePackages(knowledgePackages);
}
return knowledgeBase;
}
private static KnowledgePackage getKnowledgePackage(byte[] guiaCompilada){
if (guiaCompilada==null){
return null;
}
ByteArrayInputStream bais = new ByteArrayInputStream(guiaCompilada);
ObjectInputStream objInput = null;
KnowledgePackage knowledgePackage = null;
try {
objInput = new ObjectInputStream(bais);
knowledgePackage = (KnowledgePackage)objInput.readObject();
} catch (Exception e) {
ExceptionHandler.handle(e);
return null;
}
return knowledgePackage;
}
private static String getGuideString(Collection<GuideDTO> guides){
StringBuffer guidesStr = new StringBuffer();
for (GuideDTO guideDTO : guides) {
guidesStr.append(guideDTO.getSource());
}
return guidesStr.toString();
}
public static DroolsExecutionManager getDelegate(){
if (_instance==null){
_instance = new DroolsExecutionManager();
}
return _instance;
}
public static Long getExecutionTimeOut(){
if (getDelegate()._timeOutInMillis==null){
try {
String timeOutStr = CDSConfigurationParametersManager.getParameter(CDSConfigurationParametersManager.CDS_EXECUTION_TIMEOUT);
getDelegate()._timeOutInMillis = Long.parseLong(timeOutStr);
} catch (Exception e) {
Logger.getLogger(DroolsExecutionManager.class).info("No CDS execution timeout or errors found loading it. Timeout will be disabled.");
}
if (getDelegate()._timeOutInMillis==null){
getDelegate()._timeOutInMillis = Long.MAX_VALUE;
}
}
return getDelegate()._timeOutInMillis;
}
public static String getGuideSalienceId(String guideId){
return "$"+guideId.replaceAll("[^a-zA-Z0-9]+","")+"_salience";
}
}
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 2.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public 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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an 'AS IS' basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
*
* The Initial Developers of the Original Code are Iago Corbal and Rong Chen.
* Portions created by the Initial Developer are Copyright (C) 2012-2013
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Software distributed under the License is distributed on an 'AS IS' basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* ***** END LICENSE BLOCK *****
*/