title: How to write Stateful and Time-based rules in Drools?
body: <p>I'm trying to write a rule based engine using Drools. The rules go like this:</p>
  
  <p>Rule "Alarm": If the status is "Alarm" then send notification right away.</p>
  
  <p>Rule "Warning": If the status is "Warning" then save the device ID in memory and wait for 5 minutes. If another message with the same device ID and status "Resolved" is received within 5 minutes, then cancel this rule. Otherwise, upgrade the status to Alarm and trigger the Rule "Alarm".</p>
  
  <p>Rule "Resolved": If status is "Resolved" and device ID is already in memory, then clear the "Warning" for that device ID i.e. remove the device ID from memory.</p>
  
  <p>Rule Alarm is easy and is working. But rules Warning and Resolved are obviously not working in my code. How to write such rules in Drools?</p>
  
  <p>Here's the code I've written so far:</p>
  
  <h2>DroolsTest.java</h2>
  
  <pre><code>package com.sample;
  
  import org.drools.KnowledgeBase;
  import org.drools.KnowledgeBaseFactory;
  import org.drools.builder.KnowledgeBuilder;
  import org.drools.builder.KnowledgeBuilderError;
  import org.drools.builder.KnowledgeBuilderErrors;
  import org.drools.builder.KnowledgeBuilderFactory;
  import org.drools.builder.ResourceType;
  import org.drools.io.ResourceFactory;
  import org.drools.logger.KnowledgeRuntimeLogger;
  import org.drools.logger.KnowledgeRuntimeLoggerFactory;
  import org.drools.runtime.StatefulKnowledgeSession;
  
  public class DroolsTest {
  
      public static final void main(String[] args) {
          try {
              // load up the knowledge base
              KnowledgeBase kbase = readKnowledgeBase();
              StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
              KnowledgeRuntimeLogger logger = KnowledgeRuntimeLoggerFactory.newFileLogger(ksession, "test");
              // go !
              MachineMessage[] messages = new MachineMessage[4]; 
              messages[0] = new MachineMessage("1", "Warning");
              messages[1] = new MachineMessage("2", "Alarm");
              messages[2] = new MachineMessage("3", "Alarm");
              messages[3] = new MachineMessage("1", "Resolved");
  
              for(int i = 0; i &lt; messages.length; i++)
              {
                  ksession.insert(messages[i]);
                  ksession.fireAllRules();
                  Thread.sleep(9000);
              }
              logger.close();
          } catch (Throwable t) {
              t.printStackTrace();
          }
      }
  
      private static KnowledgeBase readKnowledgeBase() throws Exception {
          KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
          kbuilder.add(ResourceFactory.newClassPathResource("Sample.drl"), ResourceType.DRL);
          KnowledgeBuilderErrors errors = kbuilder.getErrors();
          if (errors.size() &gt; 0) {
              for (KnowledgeBuilderError error: errors) {
                  System.err.println(error);
              }
              throw new IllegalArgumentException("Could not parse knowledge.");
          }
          KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
          kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());
          return kbase;
      }
  
      public static class MachineMessage {
  
          private String deviceID;
          private String status;
  
          public MachineMessage() { }
          public MachineMessage(String deviceID, String status) {
              this.deviceID = deviceID;
              this.status = status;           
          }
  
          public String getDeviceID() {
              return this.deviceID;
          }
  
          public void setDeviceID(String deviceID) {
              this.deviceID = deviceID;
          }
  
          public String getStatus() {
              return this.status;
          }
  
          public void setStatus(String status) {
              this.status = status;
          }
      }
  }
  </code></pre>
  
  <h2>Sample.drl</h2>
  
  <pre><code>package com.sample
  
  import com.sample.DroolsTest.MachineMessage;
  
  rule "Alarm"
  when
      m : MachineMessage( status == "Alarm", myDeviceID : deviceID )
  then
      System.out.println("Send notification to technician, device in Alarm state. Device ID: " +  myDeviceID );
  end
  
  rule "Warning"
  when
      m : MachineMessage( status == "Warning", myDeviceID : deviceID )
  then
      System.out.println("Wait for 5 minutes, device in Warning state. Device ID: " + myDeviceID);        
      // if we get Machine Message with the same device ID and Resolved status in 5 minutes then 
      // we'll not trigger the following line
      m.setStatus("Alarm");
      update(m);
  end
  
  rule "Resolved"
  when
      m : MachineMessage( status == "Resolved", myDeviceID : deviceID )
  then
      System.out.println("Device has resolved its warning state. Device ID: " + myDeviceID);
  
  end
  </code></pre>
  
  <p>The actual output I'm getting is this:</p>
  
  <pre><code>    Wait for 5 minutes, device in Warning state. Device ID: 1
      Send notification to technician, device in Alarm state. Device ID: 1
      Send notification to technician, device in Alarm state. Device ID: 2
      Send notification to technician, device in Alarm state. Device ID: 3
      Device has resolved its warning state. Device ID: 1
  </code></pre>
  
  <p>Whereas I wanted:</p>
  
  <pre><code>    Wait for 5 minutes, device in Warning state. Device ID: 1
      Send notification to technician, device in Alarm state. Device ID: 2
      Send notification to technician, device in Alarm state. Device ID: 3
      Device has resolved its warning state. Device ID: 1
  </code></pre>