001package org.kuali.ole.deliver.drools;
002
003import org.apache.commons.io.FileUtils;
004import org.apache.commons.io.FilenameUtils;
005import org.apache.lucene.util.CollectionUtil;
006import org.drools.core.definitions.rule.impl.RuleImpl;
007import org.kie.api.KieServices;
008import org.kie.api.builder.KieBuilder;
009import org.kie.api.builder.KieFileSystem;
010import org.kie.api.builder.Message;
011import org.kie.api.builder.Results;
012import org.kie.api.definition.KiePackage;
013import org.kie.api.definition.rule.Rule;
014import org.kie.api.event.rule.DebugAgendaEventListener;
015import org.kie.api.runtime.KieContainer;
016import org.kie.api.runtime.KieSession;
017import org.kie.internal.io.ResourceFactory;
018import org.kuali.ole.OLEConstants;
019import org.kuali.ole.deliver.service.ParameterValueResolver;
020import org.kuali.ole.sys.context.SpringContext;
021import org.kuali.rice.core.api.config.property.ConfigContext;
022import org.kuali.rice.coreservice.api.parameter.Parameter;
023import org.kuali.rice.coreservice.api.parameter.ParameterType;
024import org.kuali.rice.coreservice.framework.parameter.ParameterService;
025import org.slf4j.Logger;
026import org.slf4j.LoggerFactory;
027
028import java.io.File;
029import java.util.*;
030
031/**
032 * Created by pvsubrah on 3/23/15.
033 */
034public class DroolsKieEngine {
035    private Logger LOG = LoggerFactory.getLogger(DroolsKieEngine.class);
036    private KieContainer kieContainer;
037    private ParameterService parameterService;
038
039    private static Boolean forceLoadPolicies = true;
040    private static final String LOAD_POLICIES_IND = "LOAD_CIRC_POLICIES_IND";
041
042    private static DroolsKieEngine droolsEngine = new DroolsKieEngine();
043
044    protected DroolsKieEngine() {
045
046    }
047
048    public static DroolsKieEngine getInstance() {
049        return droolsEngine;
050    }
051
052    /*
053    This method should be called first to load the rules repository.
054     */
055    public void initKnowledgeBase() {
056        long startTime = System.currentTimeMillis();
057        populateKnowledgeBase();
058        long endTime = System.currentTimeMillis();
059        System.out.println("Time taken to populate drools knowledgebase: " + (endTime - startTime) + " ms");
060    }
061
062    private void populateKnowledgeBase() {
063        Boolean parameterValueAsBoolean = getParameterAsBoolean();
064        if (forceLoadPolicies || parameterValueAsBoolean) {
065            readRules();
066            updateParameter();
067        }
068    }
069
070    private Boolean getParameterAsBoolean() {
071        return ParameterValueResolver.getInstance().getParameterAsBoolean(OLEConstants
072                        .APPL_ID, OLEConstants
073                        .DLVR_NMSPC,
074                OLEConstants
075                        .DLVR_CMPNT, LOAD_POLICIES_IND);
076    }
077
078    private void updateParameter() {
079        Parameter existingParameter = getParameterService().getParameter(OLEConstants.DLVR_NMSPC, OLEConstants
080                .DLVR_CMPNT, LOAD_POLICIES_IND);
081        if (existingParameter != null) {
082            Parameter.Builder updatedParameter = Parameter.Builder.create(existingParameter);
083            updatedParameter.setValue("N");
084            forceLoadPolicies = false;
085            getParameterService().updateParameter(updatedParameter.build());
086        } else {
087            Parameter.Builder newParameter = Parameter.Builder.create(OLEConstants.APPL_ID, OLEConstants.DLVR_NMSPC, OLEConstants.DLVR_CMPNT, LOAD_POLICIES_IND, ParameterType.Builder.create("CONFG"));
088            newParameter.setDescription("Set to 'Y' to have the application ingest the default circulation policies " +
089                    "upon next policy evaluation.");
090            newParameter.setValue("Y");
091            getParameterService().createParameter(newParameter.build());
092            forceLoadPolicies = false;
093        }
094    }
095
096    private void readRules() {
097
098        File rulesDirectory = FileUtils.getFile(getRulesDirectory());
099        if (null != rulesDirectory && rulesDirectory.isDirectory() && FileUtils.sizeOfDirectory(rulesDirectory) > 0) {
100            File[] files = rulesDirectory.listFiles();
101
102            KieServices kieServices = KieServices.Factory.get();
103            KieFileSystem kfs = kieServices.newKieFileSystem();
104
105            loadRules(kfs, files);
106
107            KieBuilder kieBuilder = kieServices.newKieBuilder(kfs).buildAll();
108            Results results = kieBuilder.getResults();
109
110            if (results.hasMessages(Message.Level.ERROR)) {
111               //TODO: Populate the DroolResponse object with the appropriate message.
112            }
113
114            long startTime = System.currentTimeMillis();
115            kieContainer = kieServices.newKieContainer(kieServices.getRepository().getDefaultReleaseId());
116            long endTime = System.currentTimeMillis();
117            System.out.println("Time taken to initiallize KieContainer: " + (endTime - startTime) + "ms");
118
119
120        } else {
121            LOG.error("Could not load Circulation Rules as directory doest not exist or is unreadable.");
122        }
123    }
124
125    private void loadRules(KieFileSystem kfs, File[] files) {
126        for (int i = 0; i < files.length; i++) {
127            File file = files[i];
128            if (!file.isDirectory()) {
129                if (FilenameUtils.getExtension(file.getName()).equals("drl")) {
130                    kfs.write(ResourceFactory.newFileResource(file));
131                }
132            } else {
133                File[] subDirFiles = file.listFiles();
134                loadRules(kfs, subDirFiles);
135            }
136        }
137    }
138
139    public synchronized void disposeSession(KieSession kieSession) {
140        kieSession.dispose();
141    }
142
143    public String getRulesDirectory() {
144        String rulesDirectory = ConfigContext.getCurrentContextConfig().getProperty("rules.directory");
145        return rulesDirectory;
146    }
147
148    public synchronized KieSession getSession() {
149        populateKnowledgeBase();
150        long startTime1 = System.currentTimeMillis();
151        KieSession kieSession = kieContainer.newKieSession();
152        kieSession.addEventListener(new CustomAgendaEventListener());
153        kieSession.addEventListener(new DebugAgendaEventListener());
154        long endTime1 = System.currentTimeMillis();
155
156        System.out.println("Time taken to initialize KieSession: " + (endTime1 - startTime1) + "ms");
157
158        return kieSession;
159
160    }
161
162    public ParameterService getParameterService() {
163        if (null == parameterService) {
164            parameterService = SpringContext.getBean(ParameterService.class);
165        }
166        return parameterService;
167    }
168
169    public void setParameterService(ParameterService parameterService) {
170        this.parameterService = parameterService;
171    }
172
173
174    public List<String> getRulesByAgendaGroup(List<String>agendaGroups){
175        List<String> ruleNames = new ArrayList<>();
176
177        for (Iterator iterator = kieContainer.getKieBase().getKiePackages().iterator(); iterator.hasNext(); ) {
178            KiePackage kiePackage = (KiePackage) iterator.next();
179            Collection<Rule> rules = kiePackage.getRules();
180            for (Iterator<Rule> ruleIterator = rules.iterator(); ruleIterator.hasNext(); ) {
181                Rule rule = ruleIterator.next();
182                if(agendaGroups.contains(((RuleImpl)rule).getAgendaGroup())){
183                    ruleNames.add(rule.getName());
184                }
185            }
186        }
187        Collections.sort(ruleNames);
188        return ruleNames;
189    }
190
191
192    public List<String> getAllLoadedRules(){
193        List<String> ruleNames = new ArrayList<>();
194
195        for (Iterator iterator = kieContainer.getKieBase().getKiePackages().iterator(); iterator.hasNext(); ) {
196            KiePackage kiePackage = (KiePackage) iterator.next();
197            Collection<Rule> rules = kiePackage.getRules();
198            for (Iterator<Rule> ruleIterator = rules.iterator(); ruleIterator.hasNext(); ) {
199                Rule rule = ruleIterator.next();
200                    ruleNames.add(rule.getName());
201            }
202        }
203
204        return ruleNames;
205    }
206}