Issue
I am implementing Drools in a Java Project, version 6 of the below dependencies:
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-core</artifactId>
<version>6.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>6.0.1.Final</version>
</dependency>
I intent to invoke the rules by calling a REST API, built with spring 4.3.0.RELEASE. In my config class, I am loading the drools script from database:
@Configuration
@EnableWebMvc
@EnableAsync
@ComponentScan(basePackages = "com.bla.bla.api.app")
@ImportResource({ "classpath:datasources-context.xml" })
public class AppConfig {
private static final Log log = LogFactory.getLog(AppConfig.class);
public @Autowired IServiceDao serviceDao;
The rules are then being stored in a global variable as list in a @Repository
annotated class:
@Repository
public class ServiceDao implements IServiceDao
In the above ServiceDao there is a method, invoked at the post construct stage:
@PostConstruct
private void init() throws IOException {
log.debug("Initializing ruleevaluation API Context");
// Initializing rules:
serviceDao.initializeRules();
}
, that reloads the rules from database and initializes the drools environment:
public void initializeRules() {
// 1. Load Rules:
log.debug("Initiating Rules...");
rules.clear();
rules = loadRules();
// 2. Initialize Drools Environment:
try {
initializeDrools();
} catch (DroolsParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
the method 'initializeDrools()' is implemented as below:
private void initializeDrools() throws DroolsParserException, IOException {
PackageBuilder packageBuilder = new PackageBuilder();
RuleBase ruleBase = RuleBaseFactory.newRuleBase();
// This script will contain all the rules combined in one Drools script:
String completeRulesDroolsScript = "";
// Initialize script with object imports:
completeRulesDroolsScript = ""
+ "import com.bla.model.svc.RuleRequest \r\n"
+ "import com.bla.model.svc.RuleResponse \r\n\n\n";
if (rules != null) {
if (rules.size() > 0) {
for (RuleObject ruleObject : rules) {
// Add each individual Drools rule expression to the complete Drools script:
completeRulesDroolsScript += ruleObject.getExpression();
completeRulesDroolsScript = completeRulesDroolsScript .concat("\n\n\n");
}
}
}
try {
packageBuilder.addPackageFromDrl(new
StringReader(completeRulesDroolsScript));
org.drools.core.rule.Package rulesPackage = packageBuilder.getPackage();
ruleBase.addPackage(rulesPackage);
this.setWorkingMemory(ruleBase.newStatefulSession());
}
catch(Exception e) {
log.error("!!! Could not Initialize Drools Engine !!!");
}
}
Next is the REST method, in a separate controller class:
@RestController
public class RuleEvaluationController
which simply calls the backend service from ServiceDao class:
@PostMapping(value = "/evaluateRule")
public ResponseEntity<RuleEvaluationResponse> evaluateRuleData(
@RequestBody RuleEvaluationRequest ruleEvaluationRequest) {
RuleEvaluationResponse ruleEvaluationResponse = new RuleEvaluationResponse();
try {
// 1. get the parameters list:
log.debug(ruleEvaluationRequest.toString());
RuleEvaluationResponse response = serviceDao
.evaluateRule(ruleEvaluationRequest);
log.debug("response = " + response.getEntry());
return new ResponseEntity<RuleEvaluationResponse>(response,
HttpStatus.OK);
} catch (Exception e) {
log.error("!!!!! Exception Occurred:" + e.getMessage());
// set request id, and date in millis:
ruleevaluationResponse.setRequestId(UUID.randomUUID().toString());
ruleevaluationResponse.setDate(System.currentTimeMillis());
ruleevaluationResponse.setErrorCode(Constants.FAILURE);
ruleevaluationResponse.setErrorDescription(e.getMessage());
return new ResponseEntity<RuleEvaluationResponse>(ruleevaluationResponse,
HttpStatus.OK);
}
}
Where serviceDao.evaluateRule simply inserts the request and response objects, and then fires the rules:
public RuleEvaluationResponse evaluateRule(RuleEvaluationRequest request) {
RuleEvaluationResponse response = new RuleEvaluationResponse();
this.getWorkingMemory().insert(response);
this.getWorkingMemory().insert(request);
this.getWorkingMemory().fireAllRules();
log.debug("Response = " + response.getEntry());
return response;
}
At first this all works, and I am getting the rules evaluated successfully and the expected response is obtained. However after 4 or 5 REST calls, the next rest call takes forever and never returns a response and after a while I get an out of memory error.
Can anyone tell me what am I missing here? any hints and advice are highly appreciated. Please feel free to ask for more details if the above info is insufficient.
Thanks
Solution
You didn't provide all code so maybe you have cleanup somewhere else
but it looks like you are inserting and calling .fireAllRules()
and not cleaning up. My code works with KieSession and looks more or less like this:
kSession = container.newKieSession("name");
kSession.insert(...);
kSession.insert(...);
kSession.insert(...);
kSession.fireAllRules();
Object[] objects = kSession.getObjects();
kSession.dispose();
You may have to change your code. https://docs.jboss.org/drools/release/6.0.1.Final/drools-docs/html_single/#d0e3555
The statefull session described in the doc requires deleting objects after first fireAllRules()
call. So you either create stateless session and dispose of it every time or use stateful session and delete old objects before inserting new.
I would probably go with stateless (because I am doing it already ;)
[...]
try {
packageBuilder.addPackageFromDrl(new
StringReader(completeRulesDroolsScript));
org.drools.core.rule.Package rulesPackage =
packageBuilder.getPackage();
ruleBase.addPackage(rulesPackage);
this.setWorkingMemory(ruleBase.newStatefulSession());
}
[...]
I am assuming all the heavy lifting (parsing DRL, etc.) is happening before newStatefulSession()
call and creating
a session is not 'expensive'.
Answered By - user3075118