/* * Written by Red Hat Consulting. * * * 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 com.rhc.drools.reference; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.drools.KnowledgeBase; import org.drools.command.Command; import org.drools.command.CommandFactory; import org.drools.event.rule.AfterActivationFiredEvent; import org.drools.event.rule.DefaultAgendaEventListener; import org.drools.logger.KnowledgeRuntimeLogger; import org.drools.logger.KnowledgeRuntimeLoggerFactory; import org.drools.runtime.ExecutionResults; import org.drools.runtime.StatelessKnowledgeSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A generic, reusable component that identifies all concerns related to a Stateless Drools interaction in a Java * Application. We use a simple request/response API. * */ public class StatelessDroolsComponent { private static Logger logger = LoggerFactory.getLogger( StatelessDroolsComponent.class ); // User Concern #1 private KnowledgeBaseBuilder kBaseBuilder; // User Concern #2 -- Use commands or JBPM private CommandListBuilder commandListBuilder; // User Concern #4 private ExecutionResultsTransformer resultsTransformer; // User Concern #5 Am I testing or not? Setting this value means you are testing private String fullyQualifiedLogFileName; private ConcurrentHashMap<String, List<AfterActivationFiredEvent>> firedActivations; // Name to be managed by the componentManager private String name; private int executionCount = 0; /** * Standard Constructor when using CommandLists * * @param kBaseBuilder * @param commandListBuilder * @param resultsTransformer */ public StatelessDroolsComponent( KnowledgeBaseBuilder kBaseBuilder, CommandListBuilder commandListBuilder, ExecutionResultsTransformer resultsTransformer ) { this.kBaseBuilder = kBaseBuilder; this.commandListBuilder = commandListBuilder; this.resultsTransformer = resultsTransformer; registerCompnent(); } /** * Constructor for testing w/ CommandLists. Setting the log name will activate the audit log and trigger the * capturing of fired rule events. Note: this will slow performance * * @param kBaseBuilder * @param commandListBuilder * @param resultsTransformer * @param fullyQualifiedLogFileName */ public StatelessDroolsComponent( KnowledgeBaseBuilder kBaseBuilder, CommandListBuilder commandListBuilder, ExecutionResultsTransformer resultsTransformer, String fullyQualifiedLogFileName ) { this.kBaseBuilder = kBaseBuilder; this.commandListBuilder = commandListBuilder; this.resultsTransformer = resultsTransformer; this.fullyQualifiedLogFileName = fullyQualifiedLogFileName; registerCompnent(); } /** * Nullary Constructor for use with Dependency Injection */ public StatelessDroolsComponent() { registerCompnent(); } @SuppressWarnings({ "rawtypes" }) public <Response> Response execute( DroolsRequest request, Class<Response> responseClazz ) { // logging is optional and should only be done when testing, as it slows down the engine KnowledgeRuntimeLogger droolsAuditLogger = null; List<Command> commandList = commandListBuilder.buildBusinessLogicCommandList( request ); KnowledgeBase kbase = kBaseBuilder.getKnowledgeBase(); // append the queries to the end of the list so they are executed after the business logic Set<Command> queryCommands = QueryUtils.buildQueryCommands( responseClazz ); commandList.addAll( queryCommands ); StatelessKnowledgeSession kSession = kbase.newStatelessKnowledgeSession(); // setting the audit log file name will cause the component to log and capture fired rule events if ( fullyQualifiedLogFileName != null ) { droolsAuditLogger = KnowledgeRuntimeLoggerFactory.newFileLogger( kSession, createAuditLogName() ); addFiredRulesEventListener( kSession ); } long startTime = System.currentTimeMillis(); logger.debug( "Executing " + name + "..." ); ExecutionResults results = kSession.execute( CommandFactory.newBatchExecution( commandList ) ); logger.debug( "Executing " + name + " took" + ( System.currentTimeMillis() - startTime ) + " ms" ); if ( fullyQualifiedLogFileName != null ) { droolsAuditLogger.close(); } Response response = ( resultsTransformer != null ) ? resultsTransformer.transform( results, responseClazz ) : null; return response; } /** * This is a slick way to capture all activations fired in the session so they can be interrogated by tests. This * feature is only active when the audit log name is set because it slows the engine down. * * @param kSession */ private void addFiredRulesEventListener( StatelessKnowledgeSession kSession ) { firedActivations = new ConcurrentHashMap<String, List<AfterActivationFiredEvent>>(); kSession.addEventListener( new DefaultAgendaEventListener() { @Override public void afterActivationFired( AfterActivationFiredEvent event ) { String name = event.getActivation().getRule().getName(); List<AfterActivationFiredEvent> tempList = null; if ( firedActivations.containsKey( name ) ) { tempList = firedActivations.get( name ); } else { tempList = new ArrayList<AfterActivationFiredEvent>(); } tempList.add( event ); firedActivations.put( name, tempList ); } } ); } private String createAuditLogName() { return fullyQualifiedLogFileName + "_" + incrementExecutionCount(); } private void registerCompnent() { ComponentManager.addComponent( this ); } private synchronized int incrementExecutionCount() { return this.executionCount++; } /** * Convenience method for testing to return activations from previous the previous execution. * * @return Map<Rule name, List<Activations of that rule fired>> */ public Map<String, List<AfterActivationFiredEvent>> getPreviouslyFiredActivations() { return firedActivations; } public void setKnowledgeBaseBuilder( KnowledgeBaseBuilder kBaseBuilder ) { this.kBaseBuilder = kBaseBuilder; } public KnowledgeBaseBuilder getKnowledgeBaseBuilder() { return kBaseBuilder; } public void setCommandListBuilder( CommandListBuilder commandListBuilder ) { this.commandListBuilder = commandListBuilder; } public void setResultsTransformer( ExecutionResultsTransformer resultsTransformer ) { this.resultsTransformer = resultsTransformer; } public void setFullyQualifiedLogFileName( String fullyQualifiedLogFileName ) { this.fullyQualifiedLogFileName = fullyQualifiedLogFileName; } public String getName() { return name; } public void setName( String name ) { ComponentManager.changeName( this.name, name ); this.name = name; } /* * This is here so the component manager can set the name without recursion happening since set names has to change * the name in the component manager. */ protected void updateName( String name ) { this.name = name; } }