/** * Licensed to the Austrian Association for Software Tool Integration (AASTI) * under one or more contributor license agreements. See the NOTICE file * distributed with this work for additional information regarding copyright * ownership. The AASTI licenses this file to you 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.openengsb.core.workflow.drools.internal; import java.io.StringReader; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Properties; import java.util.Set; import org.drools.KnowledgeBase; import org.drools.builder.KnowledgeBuilder; import org.drools.builder.KnowledgeBuilderFactory; import org.drools.builder.ResourceType; import org.drools.compiler.PackageBuilderConfiguration; import org.drools.definition.KnowledgePackage; import org.drools.impl.KnowledgeBaseImpl; import org.drools.io.Resource; import org.drools.io.ResourceFactory; import org.openengsb.core.workflow.api.RuleBaseException; import org.openengsb.core.workflow.api.RuleManager; import org.openengsb.core.workflow.api.model.RuleBaseElementId; import org.openengsb.core.workflow.api.model.RuleBaseElementType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.Collections2; public class RulebaseBuilder { private static final Logger LOGGER = LoggerFactory.getLogger(RulebaseBuilder.class); private static final String RULE_TEMPLATE = "rule \"%s\"\n" + "%s" + "end\n"; private final KnowledgeBase base; private final RuleManager manager; private String declarations; private Map<String, StringBuffer> packageStrings = new HashMap<String, StringBuffer>(); public RulebaseBuilder(KnowledgeBase base, RuleManager manager) { this.base = base; this.manager = manager; } /** * reloads the rulebase but keeps references intact * * @throws RuleBaseException if the rulebase contains errors */ public synchronized void reloadRulebase() throws RuleBaseException { long start = System.currentTimeMillis(); reloadDeclarations(); packageStrings.clear(); for (RuleBaseElementId id : manager.listAll(RuleBaseElementType.Function)) { String packageName = id.getPackageName(); StringBuffer packageString = getPackageString(packageName); String code = manager.get(id); packageString.append(code); } for (RuleBaseElementId id : manager.listAll(RuleBaseElementType.Rule)) { String packageName = id.getPackageName(); StringBuffer packageString = getPackageString(packageName); String code = manager.get(id); String formattedRule = String.format(RULE_TEMPLATE, id.getName(), code); packageString.append(formattedRule); } for (RuleBaseElementId id : manager.listAll(RuleBaseElementType.Process)) { getPackageString(id.getPackageName()); } Collection<KnowledgePackage> compiledPackages = new HashSet<KnowledgePackage>(); if (packageStrings.isEmpty()) { Set<String> emptySet = Collections.emptySet(); compiledPackages.addAll(compileDrlString("package dummy;\n" + declarations, emptySet)); } else { for (Map.Entry<String, StringBuffer> entry : packageStrings.entrySet()) { String packageName = entry.getKey(); StringBuffer drlCode = entry.getValue(); Collection<String> flows = queryFlows(packageName); Collection<KnowledgePackage> compiledDrlPackage = compileDrlString(drlCode.toString(), flows); compiledPackages.addAll(compiledDrlPackage); } } lockRuleBase(); clearRulebase(); base.addKnowledgePackages(compiledPackages); unlockRuleBase(); LOGGER.info("Reloading the rulebase took {}ms", System.currentTimeMillis() - start); } private Collection<String> queryFlows(final String packageName) { Collection<RuleBaseElementId> list = manager.listAll(RuleBaseElementType.Process); Collection<RuleBaseElementId> filtered = Collections2.filter(list, new Predicate<RuleBaseElementId>() { @Override public boolean apply(RuleBaseElementId input) { return input.getPackageName().equals(packageName); } }); return Collections2.transform(filtered, new Function<RuleBaseElementId, String>() { @Override public String apply(RuleBaseElementId input) { return manager.get(input); } }); } public synchronized void reloadPackage(String packageName) throws RuleBaseException { long start = System.currentTimeMillis(); reloadDeclarations(); packageStrings.clear(); StringBuffer packageString = initNewPackageString(packageName); for (RuleBaseElementId id : manager.list(RuleBaseElementType.Function, packageName)) { String code = manager.get(id); packageString.append(code); } for (RuleBaseElementId id : manager.list(RuleBaseElementType.Rule, packageName)) { String code = manager.get(id); String formattedRule = String.format(RULE_TEMPLATE, id.getName(), code); packageString.append(formattedRule); } Collection<String> flows = queryFlows(packageName); Collection<KnowledgePackage> compiledPackage = compileDrlString(packageString.toString(), flows); lockRuleBase(); if (base.getKnowledgePackage(packageName) != null) { base.removeKnowledgePackage(packageName); } base.addKnowledgePackages(compiledPackage); unlockRuleBase(); LOGGER.info("Reloading only package {} took {}ms", packageName, System.currentTimeMillis() - start); } private void unlockRuleBase() { ((KnowledgeBaseImpl) base).ruleBase.unlock(); } private void lockRuleBase() { ((KnowledgeBaseImpl) base).ruleBase.lock(); } private void clearRulebase() { for (KnowledgePackage p : base.getKnowledgePackages()) { base.removeKnowledgePackage(p.getName()); } } private StringBuffer getPackageString(String packageName) { StringBuffer result = packageStrings.get(packageName); if (result == null) { result = initNewPackageString(packageName); packageStrings.put(packageName, result); } return result; } private StringBuffer initNewPackageString(String packageName) { StringBuffer result = new StringBuffer(); result.append(String.format("package %s;", packageName)); if (declarations != null) { result.append(declarations); } return result; } private void reloadDeclarations() { StringBuffer prelude = new StringBuffer(); for (String imp : manager.listImports()) { prelude.append(String.format("import %s;\n", imp)); } for (Map.Entry<String, String> global : manager.listGlobals().entrySet()) { prelude.append(String.format("global %s %s;\n", global.getValue(), global.getKey())); } declarations = prelude.toString(); } private Collection<KnowledgePackage> compileDrlString(String content, Collection<String> flows) throws RuleBaseException { KnowledgeBuilder builder = getConfiguredBuilder(); builder.add(ResourceFactory.newReaderResource(new StringReader(content)), ResourceType.DRL); if (flows != null) { for (String drf : flows) { Resource resource = ResourceFactory.newReaderResource(new StringReader(drf)); builder.add(resource, ResourceType.DRF); } } if (builder.hasErrors()) { throw new RuleBaseException(builder.getErrors().toString()); } return builder.getKnowledgePackages(); } private KnowledgeBuilder getConfiguredBuilder() { Properties properties = new Properties(); properties.setProperty("drools.dialect.java.compiler", "JANINO"); PackageBuilderConfiguration conf = new PackageBuilderConfiguration(properties); KnowledgeBuilder builder = KnowledgeBuilderFactory.newKnowledgeBuilder(conf); return builder; } }