Mastering Drools: Build a Phone Billing Rule Engine in Java
This article walks through designing business rules for a mobile phone billing scenario, defining Java fact objects, implementing a Drools rule engine, writing DRL files, and testing the engine to demonstrate rule‑driven credit calculations.
Define Rules
First, analyze the business logic and create three rules: give a new user ¥20 credit, award ¥5 after three recharges between October and December 2014, and add ¥10 for each ¥100 topped up in a month.
Create Fact Object
Encapsulate the rule data in a Java POJO called EntityRule:
package com.core.drools;
import java.util.UUID;
/**
* EntityRule‑Model
*/
public class EntityRule {
private String username;
/** Whether for new account. */
private boolean account;
/** The number of add. */
private int addtime;
/** The sum of the current account. */
private double currentmoney;
/** The total amount added. */
private double totailaddmoney;
public void getSerialnumber(String username,double currentmoney){
System.out.println("Account:"+username+" Balance:¥"+currentmoney);
System.out.println("Serial Number:"+UUID.randomUUID().toString());
}
}Define Rule Engine
Declare an interface RuleEngine and provide an implementation that loads DRL files, creates a RuleBase, and fires rules against an EntityRule instance.
package com.core.drools;
public interface RuleEngine {
/** Initializes the rules engine. */
void init();
/** Refresh the rules engine. */
void refresh();
/** Execute the rules engine. */
void execute(final EntityRule entityRule);
} package com.core.drools;
import java.io.*;
import java.util.*;
import org.drools.*;
import org.drools.compiler.*;
import org.drools.rule.*;
import org.drools.spi.*;
public class RuleEngineImpl implements RuleEngine {
private RuleBase ruleBase;
@Override
public void init() {
System.setProperty("drools.dateformat", "yyyy-MM-dd HH:mm:ss");
ruleBase = SingleRuleFactory.getRuleBase();
try {
PackageBuilder packageBuilder = getPackageBuilderFile();
ruleBase.addPackages(packageBuilder.getPackages());
} catch (DroolsParserException | IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void refresh() {
ruleBase = SingleRuleFactory.getRuleBase();
for (Package p : ruleBase.getPackages()) {
ruleBase.removePackage(p.getName());
}
init();
}
@Override
public void execute(final EntityRule entityRule) {
if (ruleBase.getPackages() == null || ruleBase.getPackages().length == 0) return;
StatefulSession session = ruleBase.newStatefulSession();
session.insert(entityRule);
session.fireAllRules(new AgendaFilter() {
public boolean accept(Activation activation) {
return !activation.getRule().getName().contains("_test");
}
});
session.dispose();
}
private PackageBuilder getPackageBuilderFile() throws Exception {
List<String> drlFilePath = getRuleFile();
List<Reader> readers = loadRuleFile(drlFilePath);
PackageBuilder packageBuilder = new PackageBuilder();
for (Reader r : readers) {
packageBuilder.addPackageFromDrl(r);
}
if (packageBuilder.hasErrors()) {
throw new Exception(packageBuilder.getErrors().toString());
}
return packageBuilder;
}
private List<Reader> loadRuleFile(List<String> drlFilePath) throws FileNotFoundException {
if (drlFilePath == null || drlFilePath.isEmpty()) return null;
List<Reader> readers = new ArrayList<>();
for (String path : drlFilePath) {
readers.add(new FileReader(new File(path)));
}
return readers;
}
private List<String> getRuleFile(){
List<String> drlFilePath = new ArrayList<>();
String path = "D:/utils/my/DroolsProject/src/com/core/drools/drools_rule.drl";
drlFilePath.add(path);
return drlFilePath;
}
}Singleton RuleBase Factory
package com.core.drools;
import org.drools.*;
public class SingleRuleFactory {
private static RuleBase ruleBase;
public static RuleBase getRuleBase(){
return ruleBase != null ? ruleBase : RuleBaseFactory.newRuleBase();
}
}Write DRL Rule File
The DRL file defines three rules that correspond to the business scenarios.
package com.core.drools
import com.core.drools.EntityRule;
rule accountEntity
salience 100
lock-on-active true
when
$entityRule : EntityRule(account == true)
then
System.out.println("The new account:Present ¥20.0");
$entityRule.setCurrentmoney($entityRule.getCurrentmoney()+20);
$entityRule.getSerialnumber($entityRule.getUsername(), $entityRule.getCurrentmoney());
end
rule billEntity
salience 99
lock-on-active true
date-effective "2014-01-01 00:00:00"
date-expires "2014-12-31 23:59:59"
when
$entityRule : EntityRule(addtime >= 3)
then
System.out.println("Prepaid phone number reach "+$entityRule.getAddtime()+" times:Present ¥"+($entityRule.getAddtime()/3*5));
$entityRule.setCurrentmoney($entityRule.getCurrentmoney()+$entityRule.getAddtime()/3*5);
$entityRule.getSerialnumber($entityRule.getUsername(), $entityRule.getCurrentmoney());
end
rule addMoney
salience 98
lock-on-active true
when
$entityRule : EntityRule(totailaddmoney >= 100)
then
System.out.println("The account for the month top‑up total amount is "+$entityRule.getTotailaddmoney()+":Present ¥"+((int)$entityRule.getTotailaddmoney()/100*10));
$entityRule.setCurrentmoney($entityRule.getCurrentmoney()+((int)$entityRule.getTotailaddmoney()/100*10));
$entityRule.getSerialnumber($entityRule.getUsername(), $entityRule.getCurrentmoney());
endTest the Engine
package com.test;
import com.core.drools.*;
public class DroolsTest {
public static void main(String[] args) throws IOException {
RuleEngine engine = new RuleEngineImpl();
engine.init();
EntityRule entity = new EntityRule();
entity.setCurrentmoney(350d);
entity.setUsername("Candy");
entity.setAccount(true);
entity.setTotailaddmoney(350d);
entity.setAddtime(7);
engine.execute(entity);
}
}Test Results
The console output shows the sequential application of the three rules:
The new account:Present ¥20.0
Account:Candy Balance:¥370.0
Serial Number:0fd98593-caa2-444d-a4ff-b4001cfb3260
--------------------------------------------------
Prepaid phone number reach 7 times:Present ¥10
Account:Candy Balance:¥380.0
Serial Number:a118b211-c28e-4035-aa60-2f417f62b2f3
--------------------------------------------------
The account for the month top‑up total amount is 350.0: Present ¥30
Account:Candy Balance:¥410.0
Serial Number:2bfc02c2-267f-9cda-5a4196e2b7cfThis demonstration illustrates how Drools can externalize and manage changing business rules, making the application logic more flexible and maintainable.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
ITFLY8 Architecture Home
ITFLY8 Architecture Home - focused on architecture knowledge sharing and exchange, covering project management and product design. Includes large-scale distributed website architecture (high performance, high availability, caching, message queues...), design patterns, architecture patterns, big data, project management (SCRUM, PMP, Prince2), product design, and more.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
