Search code examples
javaserializationdroolsdrools-fusion

Temporal operator fails after serialization in Drools 6.5.0 and Drools 7.0.0


i am trying to implement serialization in drools. My problem is that drools fusion operators after, before,... are not serialized as expected.

My rules are working correctly if I don't use serialization and deserialization.

I attached a reproducer. Can anyone tell me what the problem is. I expect the rule to fire only once. It should fire once at 03:06:00 but it fires 3 times, once at 3:04:00 and 2 times at 3:06:00.

Event which is serialized:

package com.reproducer;

import java.io.Serializable;
import java.util.Date;

import org.apache.commons.lang3.builder.ToStringBuilder;

public class EventA implements Serializable {

/**
 * 
 */
private static final long serialVersionUID = 8129243856721618942L;
private int value;
private Date timestamp;

public EventA(Date timestamp, int value) {
    this.value = value;
    this.timestamp = timestamp;
}

public Date getTimestamp() {
    return timestamp;
}

public int getValue() {
    return value;
}

@Override
public String toString() {
        return new ToStringBuilder(this)
                .append("value", this.value)
                .append("timestamp", this.getTimestamp()).toString();
    }
}

Test:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
public class Reproducer {
    // date formatter for simulation data and tests
    private static DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @Configuration
    static class ContextConfiguration {

    }

    private KieBase kieBase;
    private static KieSession ksession;
    private static SessionPseudoClock clock;
    private KieSessionConfiguration sessionConfig;

    public byte[] serializedSession;

    @Test
    public void ruleTest() {
        List<EventA> events = getSimulationEvents();
        startKnowledgeSession(events.get(0).getTimestamp(), false);
        runSimulation(events);
    }

    private static Date parseDate(String input) {
        Date d = null;
        try {
            d = dateFormatter.parse(input);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return d;
    }

    private void runSimulation(List<EventA> events) {

        for (EventA current : events) {

            KieSession ksession2 = kieBase.newKieSession(sessionConfig, null);

            Marshaller marshaller = KieServices.Factory.get().getMarshallers().newMarshaller(kieBase);

            try {
                ByteArrayInputStream bais = new ByteArrayInputStream(serializedSession);
                ksession2 = marshaller.unmarshall(bais, sessionConfig, null);
                clock = ksession2.getSessionClock();
                bais.close();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

            long currTime = clock.getCurrentTime();
            long nextTime = current.getTimestamp().getTime();

            while (currTime <= (nextTime - 1000)) {
                clock.advanceTime(1000, TimeUnit.MILLISECONDS);
                ksession2.fireAllRules();
                currTime += 1000;
            }

            long diff = nextTime - currTime;
            if (diff > 0) {
                clock.advanceTime(diff, TimeUnit.MILLISECONDS);
            }

            ksession2.insert(current);
            ksession2.fireAllRules();

            // serialize knowledge session
            try {
                final ByteArrayOutputStream baos = new ByteArrayOutputStream();
                marshaller.marshall(baos, ksession2);
                serializedSession = baos.toByteArray();
            } catch (IOException e2) {
                e2.printStackTrace();
            }
            ksession2.halt();
            ksession2.dispose();
        }
    }

    private List<EventA> getSimulationEvents() {
        List<EventA> events = new ArrayList<EventA>();

        events.add(new EventA(parseDate("2010-01-01 02:00:00"), 0));
        events.add(new EventA(parseDate("2010-01-01 03:00:00"), 1));
        events.add(new EventA(parseDate("2010-01-01 03:01:00"), 0));
        events.add(new EventA(parseDate("2010-01-01 03:02:00"), 1));
        events.add(new EventA(parseDate("2010-01-01 03:03:00"), 0));
        events.add(new EventA(parseDate("2010-01-01 03:04:00"), 0));
        events.add(new EventA(parseDate("2010-01-01 03:05:00"), 0));
        events.add(new EventA(parseDate("2010-01-01 03:06:00"), 0));
        events.add(new EventA(parseDate("2010-01-01 03:07:00"), 0));

        return events;
    }

    private void startKnowledgeSession(Date startTime, boolean onHolidays) {
        // create configuration

        StringBuffer drlR1 = new StringBuffer();
        drlR1.append("package test\n");
        drlR1.append("dialect \"mvel\"\n");
        drlR1.append("import com.reproducer.EventA\n");
        drlR1.append("import java.util.Date\n");
        drlR1.append("declare EventA\n");
        drlR1.append("  @role(event)\n");
        drlR1.append("  @timestamp(timestamp)\n");
        drlR1.append("end\n");
        drlR1.append("rule test\n");
        drlR1.append(" when\n");
        drlR1.append("      $event : EventA(getValue() == 1)\n");
        drlR1.append("    not(EventA(getValue() == 1, this after [1ms,4m] $event))\n");
        drlR1.append(" then\n");
        drlR1.append(
                "       System.out.println(\"Fired \"+ new Date(drools.getWorkingMemory().getSessionClock().getCurrentTime()));\n");
        drlR1.append("end\n");

        kieBase = new KieHelper().addContent(drlR1.toString(), ResourceType.DRL).build(EventProcessingOption.STREAM);

        sessionConfig = KnowledgeBaseFactory.newKnowledgeSessionConfiguration();
        sessionConfig.setOption(ClockTypeOption.get(ClockType.PSEUDO_CLOCK.getId()));
        sessionConfig.setOption(TimedRuleExecutionOption.YES);
        sessionConfig.setOption(TimerJobFactoryOption.get("trackable"));
        sessionConfig.setOption(ClockTypeOption.get("pseudo"));

        ksession = kieBase.newKieSession(sessionConfig, null);

        // set clock reference
        clock = ksession.getSessionClock();
        clock.advanceTime(startTime.getTime(), TimeUnit.MILLISECONDS);

        sessionConfig = ksession.getSessionConfiguration();
        // serialize knowledge session
        try {
            Marshaller marshaller = KieServices.Factory.get().getMarshallers().newMarshaller(kieBase);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            marshaller.marshall(baos, ksession);

            serializedSession = baos.toByteArray();
        } catch (IOException e2) {
            e2.printStackTrace();
        }
    }
}

Solution

  • I opened a bug report for this problem at the JBOSS Developer Jira Bug Report. It was a bug in the drools core engine. It will be fixed with version 7.2.0Final.