회사 프로젝트 중에서 Drools 룰 엔진을 사용하는 프로젝트를 진행하면서, Drools 룰 엔진에 대한 학습 및 테스트 위해서 만든 예제를 중심으로 Spring Boot에서 Drools 룰 엔진 사용을 하는 방법에 대한 내용을 정리한 내용임.
작성된 예제 코드는 아래 GitHub 저장소에서 확인 가능하다.
kenux196/drools-springboot-ex
예제 소스에서 구현된 기능은 가상의 온도 센서의 온도 변화에 따른 디바이스(에어컨과 같은) 제어(Power on / off) 처리를 Drools 룰 엔진으로 하는 간단한 예제이다.
Drools 룰 엔진에 대한 내용은 국내에 서적도 거의 없고, 검색으로 찾을 수 있는 한글화된 사이트가 거의 없다. 검색된 내용도 대부분이 과거 버전에서 테스트된 내용이었다. Drools 룰 엔진 공식 사이트를 살펴보는 것이 좋은 방법일 것이라 생각되고, 나 역시 공식 사이트를 참고하였다.
build.gradle 파일
ext {
droolsVersion = "7.0.0.Final"
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.h2database:h2'
runtimeOnly 'org.postgresql:postgresql'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.slf4j:slf4j-api'
implementation "org.kie:kie-api:${droolsVersion}"
implementation "org.kie:kie-ci:${droolsVersion}"
implementation "org.kie:kie-spring:${droolsVersion}"
implementation "org.drools:drools-core:${droolsVersion}"
implementation "org.drools:drools-compiler:${droolsVersion}"
// implementation "org.drools:drools-mvel:${droolsVersion}"
implementation "org.drools:drools-templates:${droolsVersion}"
// implementation "org.drools:drools-bom:${droolsVersion}"
}
resources 아래에 rules 디렉토리 생성하고, xxx.drl 파일을 다음과 같이 작성한다.
import study.example.drools.domain.Device
import study.example.drools.domain.TempSensor
rule "룰 - 실내 온도가 30도 이상이면 에어컨을 켠다"
no-loop true
lock-on-active true
when
$tempSensor : TempSensor(indoorTemp >= 30)
$device : Device(operating == false)
then
modify($device) { setOperating(true) }
System.out.println($device.getDeviceInfo() + " Power On");
end
rule "룰 - 실내 온도가 30도 미만이면 에어컨 끈다"
no-loop true
lock-on-active true
when
$tempSensor : TempSensor(indoorTemp < 30)
$device : Device(operating == true)
then
modify($device) { setOperating(false) }
System.out.println($device.getDeviceInfo() + " Power Off");
end
DroolsAutoConfiguration 클래스 생성한다.
@Configuration
public class DroolsAutoConfiguration {
public static final String RULES_PATH = "rules/";
@Bean
@ConditionalOnMissingBean(KieFileSystem.class)
public KieFileSystem kieFileSystem() throws IOException {
KieFileSystem kieFileSystem = getKieServices().newKieFileSystem();
for (Resource file : getRuleFiles()) {
kieFileSystem.write(ResourceFactory.newClassPathResource(RULES_PATH + file.getFilename(), "UTF-8"));
}
return kieFileSystem;
}
private Resource[] getRuleFiles() throws IOException {
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
return resourcePatternResolver.getResources("classpath*:" + RULES_PATH + "**/*.*");
}
@Bean
@ConditionalOnMissingBean(KieContainer.class)
public KieContainer kieContainer() throws IOException {
final KieRepository kieRepository = getKieServices().getRepository();
kieRepository.addKieModule(kieRepository::getDefaultReleaseId);
KieBuilder kieBuilder = getKieServices().newKieBuilder(kieFileSystem());
kieBuilder.buildAll();
return getKieServices().newKieContainer(kieRepository.getDefaultReleaseId());
}
private KieServices getKieServices() {
return KieServices.Factory.get();
}
@Bean
@ConditionalOnMissingBean(KieBase.class)
public KieBase kieBase() throws IOException {
return kieContainer().getKieBase();
}
@Bean
@ConditionalOnMissingBean(KieSession.class)
public KieSession kieSession() throws IOException {
return kieContainer().newKieSession();
}
@Bean
@ConditionalOnMissingBean(KModuleBeanFactoryPostProcessor.class)
public KModuleBeanFactoryPostProcessor kiePostProcessor() {
return new KModuleBeanFactoryPostProcessor();
}
}
본 예제에서는 온도 센서의 온도 값에 따라 에어컨 제어를 하는 모델의 예제이므로 Device, TempSensor class 추가하였다.
@Data
public class Device {
private Long id;
private DeviceType type;
private Boolean operating;
public String getDeviceName() {
return id + "번 디바이스";
}
public static Device createAirConditioner(long id, boolean operating) {
return new Device(id, DeviceType.AIR_CONDITIONER, operating);
}
public Device(long id, DeviceType type, boolean operating) {
this.id = id;
this.type = type;
this.operating = false;
}
public String getDeviceInfo() {
return id + "번 디바이스 : " + type.getName();
}
}
@Data
@ToString
public class TempSensor {
private int indoorTemp;
}
Drools 룰 엔진을 이용해서 Device 제어 기능을 제공하는 서비스 구현
@Slf4j
@Service
@RequiredArgsConstructor
public class DeviceService {
private final KieContainer kieContainer;
private final DeviceRepository deviceRepository;
private KieSession kieSession;
private final TempSensor tempSensor = new TempSensor();
@PostConstruct
private void initService() {
kieSession = kieContainer.newKieSession();
kieSession.addEventListener(new CustomAgendaEventListener());
kieSession.addEventListener(new CustomRuleRunTimeEventListener());
kieSession.addEventListener(new CustomProcessEventListener());
}
public FactHandle addDevice(Device device) {
deviceRepository.addDevice(device);
FactHandle factHandle = kieSession.insert(device);
kieSession.fireAllRules();
printFactSize("기기 추가 => " + device.getDeviceInfo(), false);
return factHandle;
}
public void updateSensorData(int temp) {
log.debug("온도 센서 값 변경 = " + temp);
tempSensor.setIndoorTemp(temp);
FactHandle factHandle = kieSession.getFactHandle(tempSensor);
if (factHandle != null) {
kieSession.update(factHandle, tempSensor); // update exist fact
log.debug("온도 센서 Fact 업데이트");
} else {
kieSession.insert(tempSensor); // insert new fact
log.debug("온도 센서 Fact 추가");
}
kieSession.fireAllRules();
printFactSize("온도 값 변경 완료", false);
}
public Collection<FactHandle> getFactHandles() {
return kieSession.getFactHandles();
}
private void printFactSize(String msg, boolean showFactHandleInfo) {
log.info(msg + " ==> kieSession.getFactCount() = " + kieSession.getFactCount());
if (showFactHandleInfo) {
Collection<FactHandle> factHandles = kieSession.getFactHandles();
for (FactHandle factHandle : factHandles) {
log.info("factHandle = " + factHandle.toExternalForm());
}
}
}
public List<Device> getDevices() {
return deviceRepository.getDeviceList();
}
}
@SpringBootTest
class DeviceServiceTest {
@Autowired
DeviceService deviceService;
@Test
@DisplayName("기기 추가 테스트")
void addDeviceTest() {
final Device device = Device.createAirConditioner(1, false);
deviceService.addDevice(device);
final Collection<FactHandle> factHandles = deviceService.getFactHandles();
assertThat(factHandles).hasSize(1);
}
@Test
@DisplayName("온도가 30도 이상이면 에어컨 켠다.")
void airConditionerOnTest() {
final Device device = Device.createAirConditioner(1, false);
deviceService.addDevice(device);
deviceService.updateSensorData(35);
assertThat(device.getOperating()).isTrue();
}
@Test
@DisplayName("온도가 30도 미만이면 에어컨 끈다")
void airConditionerOffTest() {
final Device device = Device.createAirConditioner(1, true);
deviceService.addDevice(device);
deviceService.updateSensorData(24);
assertThat(device.getOperating()).isFalse();
}
}
아래는 온도 센서 값이 30도 이상인 경우에 대한 테스트와 결과이다
@Test
@DisplayName("온도가 30도 이상이면 에어컨 켠다.")
void airConditionerOnTest() {
final Device device = Device.createAirConditioner(1, false);
deviceService.addDevice(device);
deviceService.updateSensorData(35);
assertThat(device.getOperating()).isTrue();
}
objectInserted event.getObject().getClass() = class study.example.drools.domain.Device
objectInserted event.getFactHandle() = [fact 0:1:662786209:-314770286:1:DEFAULT:NON_TRAIT:study.example.drools.domain.Device:Device(id=1, type=AIR_CONDITIONER, operating=false, dummy=[{"name":null,"nameDisplayCoordinate":null,"type":null,"mappedType":null,"description":null,"parentId":null,"registrationStatus":null,"configuration":{"mobilityType":null,"remoteControl":"Permit","time":null,"locationType":null},"modes":[{"id":"AirConditioner_Indoor_IndoorMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_ErvMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_DhwMode","description":null,"enabled":null,"value":"Standard"}],"operations":[{"id":"AirConditioner_Indoor_IndoorPower","value":"on"},{"id":"AirConditioner_Indoor_ErvPower","value":"on"},{"id":"AirConditioner_Indoor_DhwPower","value":"on"}],"temperatures":[{"id":"AirConditioner_Indoor_IndoorSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_WateroutSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_DhwSetTemp","enabled":null,"unit":null,"desired":40.0}],"meters":null,"lights":null,"id":null,"airConditioner":null}, {"name":null,"nameDisplayCoordinate":null,"type":null,"mappedType":null,"description":null,"parentId":null,"registrationStatus":null,"configuration":{"mobilityType":null,"remoteControl":"Permit","time":null,"locationType":null},"modes":[{"id":"AirConditioner_Indoor_IndoorMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_ErvMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_DhwMode","description":null,"enabled":null,"value":"Standard"}],"operations":[{"id":"AirConditioner_Indoor_IndoorPower","value":"on"},{"id":"AirConditioner_Indoor_ErvPower","value":"on"},{"id":"AirConditioner_Indoor_DhwPower","value":"on"}],"temperatures":[{"id":"AirConditioner_Indoor_IndoorSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_WateroutSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_DhwSetTemp","enabled":null,"unit":null,"desired":40.0}],"meters":null,"lights":null,"id":null,"airConditioner":null}, {"name":null,"nameDisplayCoordinate":null,"type":null,"mappedType":null,"description":null,"parentId":null,"registrationStatus":null,"configuration":{"mobilityType":null,"remoteControl":"Permit","time":null,"locationType":null},"modes":[{"id":"AirConditioner_Indoor_IndoorMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_ErvMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_DhwMode","description":null,"enabled":null,"value":"Standard"}],"operations":[{"id":"AirConditioner_Indoor_IndoorPower","value":"on"},{"id":"AirConditioner_Indoor_ErvPower","value":"on"},{"id":"AirConditioner_Indoor_DhwPower","value":"on"}],"temperatures":[{"id":"AirConditioner_Indoor_IndoorSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_WateroutSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_DhwSetTemp","enabled":null,"unit":null,"desired":40.0}],"meters":null,"lights":null,"id":null,"airConditioner":null}, {"name":null,"nameDisplayCoordinate":null,"type":null,"mappedType":null,"description":null,"parentId":null,"registrationStatus":null,"configuration":{"mobilityType":null,"remoteControl":"Permit","time":null,"locationType":null},"modes":[{"id":"AirConditioner_Indoor_IndoorMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_ErvMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_DhwMode","description":null,"enabled":null,"value":"Standard"}],"operations":[{"id":"AirConditioner_Indoor_IndoorPower","value":"on"},{"id":"AirConditioner_Indoor_ErvPower","value":"on"},{"id":"AirConditioner_Indoor_DhwPower","value":"on"}],"temperatures":[{"id":"AirConditioner_Indoor_IndoorSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_WateroutSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_DhwSetTemp","enabled":null,"unit":null,"desired":40.0}],"meters":null,"lights":null,"id":null,"airConditioner":null}, {"name":null,"nameDisplayCoordinate":null,"type":null,"mappedType":null,"description":null,"parentId":null,"registrationStatus":null,"configuration":{"mobilityType":null,"remoteControl":"Permit","time":null,"locationType":null},"modes":[{"id":"AirConditioner_Indoor_IndoorMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_ErvMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_DhwMode","description":null,"enabled":null,"value":"Standard"}],"operations":[{"id":"AirConditioner_Indoor_IndoorPower","value":"on"},{"id":"AirConditioner_Indoor_ErvPower","value":"on"},{"id":"AirConditioner_Indoor_DhwPower","value":"on"}],"temperatures":[{"id":"AirConditioner_Indoor_IndoorSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_WateroutSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_DhwSetTemp","enabled":null,"unit":null,"desired":40.0}],"meters":null,"lights":null,"id":null,"airConditioner":null}, {"name":null,"nameDisplayCoordinate":null,"type":null,"mappedType":null,"description":null,"parentId":null,"registrationStatus":null,"configuration":{"mobilityType":null,"remoteControl":"Permit","time":null,"locationType":null},"modes":[{"id":"AirConditioner_Indoor_IndoorMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_ErvMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_DhwMode","description":null,"enabled":null,"value":"Standard"}],"operations":[{"id":"AirConditioner_Indoor_IndoorPower","value":"on"},{"id":"AirConditioner_Indoor_ErvPower","value":"on"},{"id":"AirConditioner_Indoor_DhwPower","value":"on"}],"temperatures":[{"id":"AirConditioner_Indoor_IndoorSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_WateroutSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_DhwSetTemp","enabled":null,"unit":null,"desired":40.0}],"meters":null,"lights":null,"id":null,"airConditioner":null}, {"name":null,"nameDisplayCoordinate":null,"type":null,"mappedType":null,"description":null,"parentId":null,"registrationStatus":null,"configuration":{"mobilityType":null,"remoteControl":"Permit","time":null,"locationType":null},"modes":[{"id":"AirConditioner_Indoor_IndoorMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_ErvMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_DhwMode","description":null,"enabled":null,"value":"Standard"}],"operations":[{"id":"AirConditioner_Indoor_IndoorPower","value":"on"},{"id":"AirConditioner_Indoor_ErvPower","value":"on"},{"id":"AirConditioner_Indoor_DhwPower","value":"on"}],"temperatures":[{"id":"AirConditioner_Indoor_IndoorSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_WateroutSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_DhwSetTemp","enabled":null,"unit":null,"desired":40.0}],"meters":null,"lights":null,"id":null,"airConditioner":null}, {"name":null,"nameDisplayCoordinate":null,"type":null,"mappedType":null,"description":null,"parentId":null,"registrationStatus":null,"configuration":{"mobilityType":null,"remoteControl":"Permit","time":null,"locationType":null},"modes":[{"id":"AirConditioner_Indoor_IndoorMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_ErvMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_DhwMode","description":null,"enabled":null,"value":"Standard"}],"operations":[{"id":"AirConditioner_Indoor_IndoorPower","value":"on"},{"id":"AirConditioner_Indoor_ErvPower","value":"on"},{"id":"AirConditioner_Indoor_DhwPower","value":"on"}],"temperatures":[{"id":"AirConditioner_Indoor_IndoorSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_WateroutSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_DhwSetTemp","enabled":null,"unit":null,"desired":40.0}],"meters":null,"lights":null,"id":null,"airConditioner":null}, {"name":null,"nameDisplayCoordinate":null,"type":null,"mappedType":null,"description":null,"parentId":null,"registrationStatus":null,"configuration":{"mobilityType":null,"remoteControl":"Permit","time":null,"locationType":null},"modes":[{"id":"AirConditioner_Indoor_IndoorMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_ErvMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_DhwMode","description":null,"enabled":null,"value":"Standard"}],"operations":[{"id":"AirConditioner_Indoor_IndoorPower","value":"on"},{"id":"AirConditioner_Indoor_ErvPower","value":"on"},{"id":"AirConditioner_Indoor_DhwPower","value":"on"}],"temperatures":[{"id":"AirConditioner_Indoor_IndoorSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_WateroutSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_DhwSetTemp","enabled":null,"unit":null,"desired":40.0}],"meters":null,"lights":null,"id":null,"airConditioner":null}, {"name":null,"nameDisplayCoordinate":null,"type":null,"mappedType":null,"description":null,"parentId":null,"registrationStatus":null,"configuration":{"mobilityType":null,"remoteControl":"Permit","time":null,"locationType":null},"modes":[{"id":"AirConditioner_Indoor_IndoorMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_ErvMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_DhwMode","description":null,"enabled":null,"value":"Standard"}],"operations":[{"id":"AirConditioner_Indoor_IndoorPower","value":"on"},{"id":"AirConditioner_Indoor_ErvPower","value":"on"},{"id":"AirConditioner_Indoor_DhwPower","value":"on"}],"temperatures":[{"id":"AirConditioner_Indoor_IndoorSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_WateroutSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_DhwSetTemp","enabled":null,"unit":null,"desired":40.0}],"meters":null,"lights":null,"id":null,"airConditioner":null}], base={"name":null,"nameDisplayCoordinate":null,"type":null,"mappedType":null,"description":null,"parentId":null,"registrationStatus":null,"configuration":{"mobilityType":null,"remoteControl":"Permit","time":null,"locationType":null},"modes":[{"id":"AirConditioner_Indoor_IndoorMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_ErvMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_DhwMode","description":null,"enabled":null,"value":"Standard"}],"operations":[{"id":"AirConditioner_Indoor_IndoorPower","value":"on"},{"id":"AirConditioner_Indoor_ErvPower","value":"on"},{"id":"AirConditioner_Indoor_DhwPower","value":"on"}],"temperatures":[{"id":"AirConditioner_Indoor_IndoorSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_WateroutSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_DhwSetTemp","enabled":null,"unit":null,"desired":40.0}],"meters":null,"lights":null,"id":null,"airConditioner":null})]
기기 추가 => 1번 디바이스 : AirConditioner ==> kieSession.getFactCount() = 1
objectInserted event.getObject().getClass() = class study.example.drools.domain.TempSensor
objectInserted event.getFactHandle() = [fact 0:2:660626311:94:2:DEFAULT:NON_TRAIT:study.example.drools.domain.TempSensor:TempSensor(indoorTemp=35)]
matchCreated event.getMatch() = [Rule name=룰 - 실내 온도가 30도 이상이면 에어컨을 켠다, agendaGroup=MAIN, salience=0, no-loop=true]
beforeMatchFired event.getMatch() = [Rule name=룰 - 실내 온도가 30도 이상이면 에어컨을 켠다, agendaGroup=MAIN, salience=0, no-loop=true]
objectUpdated event.getObject().getClass() = class study.example.drools.domain.Device
objectUpdated event.getFactHandle() = [fact 0:1:662786209:-314770286:3:DEFAULT:NON_TRAIT:study.example.drools.domain.Device:Device(id=1, type=AIR_CONDITIONER, operating=true, dummy=[{"name":null,"nameDisplayCoordinate":null,"type":null,"mappedType":null,"description":null,"parentId":null,"registrationStatus":null,"configuration":{"mobilityType":null,"remoteControl":"Permit","time":null,"locationType":null},"modes":[{"id":"AirConditioner_Indoor_IndoorMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_ErvMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_DhwMode","description":null,"enabled":null,"value":"Standard"}],"operations":[{"id":"AirConditioner_Indoor_IndoorPower","value":"on"},{"id":"AirConditioner_Indoor_ErvPower","value":"on"},{"id":"AirConditioner_Indoor_DhwPower","value":"on"}],"temperatures":[{"id":"AirConditioner_Indoor_IndoorSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_WateroutSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_DhwSetTemp","enabled":null,"unit":null,"desired":40.0}],"meters":null,"lights":null,"id":null,"airConditioner":null}, {"name":null,"nameDisplayCoordinate":null,"type":null,"mappedType":null,"description":null,"parentId":null,"registrationStatus":null,"configuration":{"mobilityType":null,"remoteControl":"Permit","time":null,"locationType":null},"modes":[{"id":"AirConditioner_Indoor_IndoorMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_ErvMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_DhwMode","description":null,"enabled":null,"value":"Standard"}],"operations":[{"id":"AirConditioner_Indoor_IndoorPower","value":"on"},{"id":"AirConditioner_Indoor_ErvPower","value":"on"},{"id":"AirConditioner_Indoor_DhwPower","value":"on"}],"temperatures":[{"id":"AirConditioner_Indoor_IndoorSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_WateroutSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_DhwSetTemp","enabled":null,"unit":null,"desired":40.0}],"meters":null,"lights":null,"id":null,"airConditioner":null}, {"name":null,"nameDisplayCoordinate":null,"type":null,"mappedType":null,"description":null,"parentId":null,"registrationStatus":null,"configuration":{"mobilityType":null,"remoteControl":"Permit","time":null,"locationType":null},"modes":[{"id":"AirConditioner_Indoor_IndoorMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_ErvMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_DhwMode","description":null,"enabled":null,"value":"Standard"}],"operations":[{"id":"AirConditioner_Indoor_IndoorPower","value":"on"},{"id":"AirConditioner_Indoor_ErvPower","value":"on"},{"id":"AirConditioner_Indoor_DhwPower","value":"on"}],"temperatures":[{"id":"AirConditioner_Indoor_IndoorSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_WateroutSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_DhwSetTemp","enabled":null,"unit":null,"desired":40.0}],"meters":null,"lights":null,"id":null,"airConditioner":null}, {"name":null,"nameDisplayCoordinate":null,"type":null,"mappedType":null,"description":null,"parentId":null,"registrationStatus":null,"configuration":{"mobilityType":null,"remoteControl":"Permit","time":null,"locationType":null},"modes":[{"id":"AirConditioner_Indoor_IndoorMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_ErvMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_DhwMode","description":null,"enabled":null,"value":"Standard"}],"operations":[{"id":"AirConditioner_Indoor_IndoorPower","value":"on"},{"id":"AirConditioner_Indoor_ErvPower","value":"on"},{"id":"AirConditioner_Indoor_DhwPower","value":"on"}],"temperatures":[{"id":"AirConditioner_Indoor_IndoorSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_WateroutSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_DhwSetTemp","enabled":null,"unit":null,"desired":40.0}],"meters":null,"lights":null,"id":null,"airConditioner":null}, {"name":null,"nameDisplayCoordinate":null,"type":null,"mappedType":null,"description":null,"parentId":null,"registrationStatus":null,"configuration":{"mobilityType":null,"remoteControl":"Permit","time":null,"locationType":null},"modes":[{"id":"AirConditioner_Indoor_IndoorMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_ErvMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_DhwMode","description":null,"enabled":null,"value":"Standard"}],"operations":[{"id":"AirConditioner_Indoor_IndoorPower","value":"on"},{"id":"AirConditioner_Indoor_ErvPower","value":"on"},{"id":"AirConditioner_Indoor_DhwPower","value":"on"}],"temperatures":[{"id":"AirConditioner_Indoor_IndoorSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_WateroutSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_DhwSetTemp","enabled":null,"unit":null,"desired":40.0}],"meters":null,"lights":null,"id":null,"airConditioner":null}, {"name":null,"nameDisplayCoordinate":null,"type":null,"mappedType":null,"description":null,"parentId":null,"registrationStatus":null,"configuration":{"mobilityType":null,"remoteControl":"Permit","time":null,"locationType":null},"modes":[{"id":"AirConditioner_Indoor_IndoorMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_ErvMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_DhwMode","description":null,"enabled":null,"value":"Standard"}],"operations":[{"id":"AirConditioner_Indoor_IndoorPower","value":"on"},{"id":"AirConditioner_Indoor_ErvPower","value":"on"},{"id":"AirConditioner_Indoor_DhwPower","value":"on"}],"temperatures":[{"id":"AirConditioner_Indoor_IndoorSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_WateroutSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_DhwSetTemp","enabled":null,"unit":null,"desired":40.0}],"meters":null,"lights":null,"id":null,"airConditioner":null}, {"name":null,"nameDisplayCoordinate":null,"type":null,"mappedType":null,"description":null,"parentId":null,"registrationStatus":null,"configuration":{"mobilityType":null,"remoteControl":"Permit","time":null,"locationType":null},"modes":[{"id":"AirConditioner_Indoor_IndoorMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_ErvMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_DhwMode","description":null,"enabled":null,"value":"Standard"}],"operations":[{"id":"AirConditioner_Indoor_IndoorPower","value":"on"},{"id":"AirConditioner_Indoor_ErvPower","value":"on"},{"id":"AirConditioner_Indoor_DhwPower","value":"on"}],"temperatures":[{"id":"AirConditioner_Indoor_IndoorSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_WateroutSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_DhwSetTemp","enabled":null,"unit":null,"desired":40.0}],"meters":null,"lights":null,"id":null,"airConditioner":null}, {"name":null,"nameDisplayCoordinate":null,"type":null,"mappedType":null,"description":null,"parentId":null,"registrationStatus":null,"configuration":{"mobilityType":null,"remoteControl":"Permit","time":null,"locationType":null},"modes":[{"id":"AirConditioner_Indoor_IndoorMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_ErvMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_DhwMode","description":null,"enabled":null,"value":"Standard"}],"operations":[{"id":"AirConditioner_Indoor_IndoorPower","value":"on"},{"id":"AirConditioner_Indoor_ErvPower","value":"on"},{"id":"AirConditioner_Indoor_DhwPower","value":"on"}],"temperatures":[{"id":"AirConditioner_Indoor_IndoorSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_WateroutSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_DhwSetTemp","enabled":null,"unit":null,"desired":40.0}],"meters":null,"lights":null,"id":null,"airConditioner":null}, {"name":null,"nameDisplayCoordinate":null,"type":null,"mappedType":null,"description":null,"parentId":null,"registrationStatus":null,"configuration":{"mobilityType":null,"remoteControl":"Permit","time":null,"locationType":null},"modes":[{"id":"AirConditioner_Indoor_IndoorMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_ErvMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_DhwMode","description":null,"enabled":null,"value":"Standard"}],"operations":[{"id":"AirConditioner_Indoor_IndoorPower","value":"on"},{"id":"AirConditioner_Indoor_ErvPower","value":"on"},{"id":"AirConditioner_Indoor_DhwPower","value":"on"}],"temperatures":[{"id":"AirConditioner_Indoor_IndoorSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_WateroutSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_DhwSetTemp","enabled":null,"unit":null,"desired":40.0}],"meters":null,"lights":null,"id":null,"airConditioner":null}, {"name":null,"nameDisplayCoordinate":null,"type":null,"mappedType":null,"description":null,"parentId":null,"registrationStatus":null,"configuration":{"mobilityType":null,"remoteControl":"Permit","time":null,"locationType":null},"modes":[{"id":"AirConditioner_Indoor_IndoorMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_ErvMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_DhwMode","description":null,"enabled":null,"value":"Standard"}],"operations":[{"id":"AirConditioner_Indoor_IndoorPower","value":"on"},{"id":"AirConditioner_Indoor_ErvPower","value":"on"},{"id":"AirConditioner_Indoor_DhwPower","value":"on"}],"temperatures":[{"id":"AirConditioner_Indoor_IndoorSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_WateroutSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_DhwSetTemp","enabled":null,"unit":null,"desired":40.0}],"meters":null,"lights":null,"id":null,"airConditioner":null}], base={"name":null,"nameDisplayCoordinate":null,"type":null,"mappedType":null,"description":null,"parentId":null,"registrationStatus":null,"configuration":{"mobilityType":null,"remoteControl":"Permit","time":null,"locationType":null},"modes":[{"id":"AirConditioner_Indoor_IndoorMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_ErvMode","description":null,"enabled":null,"value":"Auto"},{"id":"AirConditioner_Indoor_DhwMode","description":null,"enabled":null,"value":"Standard"}],"operations":[{"id":"AirConditioner_Indoor_IndoorPower","value":"on"},{"id":"AirConditioner_Indoor_ErvPower","value":"on"},{"id":"AirConditioner_Indoor_DhwPower","value":"on"}],"temperatures":[{"id":"AirConditioner_Indoor_IndoorSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_WateroutSetTemp","enabled":null,"unit":null,"desired":24.0},{"id":"AirConditioner_Indoor_DhwSetTemp","enabled":null,"unit":null,"desired":40.0}],"meters":null,"lights":null,"id":null,"airConditioner":null})]
objectUpdated event.getRule().getName() = 룰 - 실내 온도가 30도 이상이면 에어컨을 켠다
afterMatchFired event.getMatch() = [Rule name=룰 - 실내 온도가 30도 이상이면 에어컨을 켠다, agendaGroup=MAIN, salience=0, no-loop=true]
온도 값 변경 완료 ==> kieSession.getFactCount() = 2
이상으로 간단하게 Drools 룰 엔진을 스프링 부트에 적용해서 테스트 해 보았다. 개인적으로 업무가 아니었으면, 전혀 몰랐을 내용인데, 경험해 볼 수 있어서 좋지만, 깊은 내용은 조금 더 공부하고 경험해봐야 할 것 같다.
Junit 테스트에서는 잘 동작하였는데, Controller를 붙여서 웹 서비스 형태로 동작을 시키니 룰 매칭 동작하지 않는 이슈가 있었다.
확인해 보니, spring-boot-devtools 설정되어 있으면 동작하지 않는다는 내용이 검색되어서 해당 dependency를 제거하니 잘 동작함. 관련 내용은 아래 링크에서 확인 가능하다.
No rules are fired in helloworld using Spring rest controller!
그리고, 이 이슈는 drools 7.26.0.Final 버전에서 수정되어서, 예제 소스에서는 해당 버전으로 변경하였다.