[jboss-svn-commits] JBL Code SVN: r30429 - in labs/jbossrules/trunk: drools-compiler/src/main/java/org/drools/rule/builder and 9 other directories.

jboss-svn-commits at lists.jboss.org jboss-svn-commits at lists.jboss.org
Wed Dec 2 00:00:54 EST 2009


Author: mark.proctor at jboss.com
Date: 2009-12-02 00:00:53 -0500 (Wed, 02 Dec 2009)
New Revision: 30429

Added:
   labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/CompositeMaxDurationTimer.java
   labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/CompositeMaxDurationTrigger.java
   labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/CronTimer.java
   labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/DurationTimer.java
   labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/IntervalTimer.java
   labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/IntervalTrigger.java
   labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/Timer.java
Modified:
   labs/jbossrules/trunk/drools-compiler/.classpath
   labs/jbossrules/trunk/drools-compiler/.project
   labs/jbossrules/trunk/drools-compiler/src/main/java/org/drools/rule/builder/RuleBuilder.java
   labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/integrationtests/CepEspTest.java
   labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/integrationtests/ExecutionFlowControlTest.java
   labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/rule/builder/dialect/java/RuleBuilderTest.java
   labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/Scheduler.java
   labs/jbossrules/trunk/drools-core/src/main/java/org/drools/process/instance/timer/TimerManager.java
   labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/RuleTerminalNode.java
   labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/builder/PatternBuilder.java
   labs/jbossrules/trunk/drools-core/src/main/java/org/drools/rule/Rule.java
   labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/CronTrigger.java
   labs/jbossrules/trunk/drools-core/src/test/java/org/drools/reteoo/AgendaTest.java
   labs/jbossrules/trunk/drools-core/src/test/java/org/drools/reteoo/SchedulerTest.java
Log:
JBRULES-1944 Adding crontab type functionality for repetitive rules
-Add full working semantics with tests for cron and interval based timers

Modified: labs/jbossrules/trunk/drools-compiler/.classpath
===================================================================
--- labs/jbossrules/trunk/drools-compiler/.classpath	2009-12-01 20:57:44 UTC (rev 30428)
+++ labs/jbossrules/trunk/drools-compiler/.classpath	2009-12-02 05:00:53 UTC (rev 30429)
@@ -1,31 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
 <classpath>
-  <classpathentry kind="src" path="src/main/java"/>
-  <classpathentry kind="src" path="src/main/resources" excluding="**/*.java"/>
-  <classpathentry kind="src" path="src/test/java" output="target/test-classes"/>
-  <classpathentry kind="src" path="src/test/resources" output="target/test-classes" including="**" excluding="**/*.java"/>
-  <classpathentry kind="output" path="target/classes"/>
-  <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-  <classpathentry kind="var" path="M2_REPO/antlr/antlr/2.7.7/antlr-2.7.7.jar"/>
-  <classpathentry kind="var" path="M2_REPO/org/antlr/antlr/3.1.1/antlr-3.1.1.jar"/>
-  <classpathentry kind="var" path="M2_REPO/org/antlr/antlr-runtime/3.1.1/antlr-runtime-3.1.1.jar"/>
-  <classpathentry kind="var" path="M2_REPO/cglib/cglib-nodep/2.1_3/cglib-nodep-2.1_3.jar"/>
-  <classpathentry kind="var" path="M2_REPO/org/eclipse/jdt/core/3.4.2.v_883_R34x/core-3.4.2.v_883_R34x.jar"/>
-  <classpathentry kind="var" path="M2_REPO/org/drools/drools-api/5.1.0.SNAPSHOT/drools-api-5.1.0.SNAPSHOT.jar" sourcepath="M2_REPO/org/drools/drools-api/5.1.0.SNAPSHOT/drools-api-5.1.0.SNAPSHOT-sources.jar"/>
-  <classpathentry kind="var" path="M2_REPO/org/drools/drools-core/5.1.0.SNAPSHOT/drools-core-5.1.0.SNAPSHOT.jar" sourcepath="M2_REPO/org/drools/drools-core/5.1.0.SNAPSHOT/drools-core-5.1.0.SNAPSHOT-sources.jar"/>
-  <classpathentry kind="var" path="M2_REPO/org/hamcrest/hamcrest-core/1.1/hamcrest-core-1.1.jar"/>
-  <classpathentry kind="var" path="M2_REPO/org/hamcrest/hamcrest-library/1.1/hamcrest-library-1.1.jar"/>
-  <classpathentry kind="var" path="M2_REPO/janino/janino/2.5.15/janino-2.5.15.jar"/>
-  <classpathentry kind="var" path="M2_REPO/org/mortbay/jetty/jetty/6.1.15/jetty-6.1.15.jar" sourcepath="M2_REPO/org/mortbay/jetty/jetty/6.1.15/jetty-6.1.15-sources.jar"/>
-  <classpathentry kind="var" path="M2_REPO/org/mortbay/jetty/jetty-embedded/6.1.15/jetty-embedded-6.1.15.jar"/>
-  <classpathentry kind="var" path="M2_REPO/org/mortbay/jetty/jetty-util/6.1.15/jetty-util-6.1.15.jar"/>
-  <classpathentry kind="var" path="M2_REPO/org/jmock/jmock/2.5.1/jmock-2.5.1.jar"/>
-  <classpathentry kind="var" path="M2_REPO/org/jmock/jmock-legacy/2.5.1/jmock-legacy-2.5.1.jar"/>
-  <classpathentry kind="var" path="M2_REPO/joda-time/joda-time/1.6/joda-time-1.6.jar"/>
-  <classpathentry kind="var" path="M2_REPO/javax/servlet/jsp/jsp-api/2.1/jsp-api-2.1.jar"/>
-  <classpathentry kind="var" path="M2_REPO/junit/junit/4.6/junit-4.6.jar"/>
-  <classpathentry kind="var" path="M2_REPO/org/mvel/mvel2/2.0.12/mvel2-2.0.12.jar"/>
-  <classpathentry kind="var" path="M2_REPO/org/objenesis/objenesis/1.0/objenesis-1.0.jar"/>
-  <classpathentry kind="var" path="M2_REPO/org/mortbay/jetty/servlet-api/2.5-20081211/servlet-api-2.5-20081211.jar"/>
-  <classpathentry kind="var" path="M2_REPO/xpp3/xpp3_min/1.1.4c/xpp3_min-1.1.4c.jar"/>
-  <classpathentry kind="var" path="M2_REPO/com/thoughtworks/xstream/xstream/1.3.1/xstream-1.3.1.jar"/>
-</classpath>
\ No newline at end of file
+	<classpathentry kind="src" path="src/main/java"/>
+	<classpathentry excluding="**/*.java" kind="src" path="src/main/resources"/>
+	<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
+	<classpathentry excluding="**/*.java" including="**" kind="src" output="target/test-classes" path="src/test/resources"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="var" path="M2_REPO/antlr/antlr/2.7.7/antlr-2.7.7.jar"/>
+	<classpathentry kind="var" path="M2_REPO/org/antlr/antlr/3.1.1/antlr-3.1.1.jar"/>
+	<classpathentry kind="var" path="M2_REPO/org/antlr/antlr-runtime/3.1.1/antlr-runtime-3.1.1.jar"/>
+	<classpathentry kind="var" path="M2_REPO/cglib/cglib-nodep/2.1_3/cglib-nodep-2.1_3.jar"/>
+	<classpathentry kind="var" path="M2_REPO/org/eclipse/jdt/core/3.4.2.v_883_R34x/core-3.4.2.v_883_R34x.jar"/>
+	<classpathentry kind="src" path="/drools-api"/>
+	<classpathentry kind="src" path="/drools-core"/>
+	<classpathentry kind="var" path="M2_REPO/org/hamcrest/hamcrest-core/1.1/hamcrest-core-1.1.jar"/>
+	<classpathentry kind="var" path="M2_REPO/org/hamcrest/hamcrest-library/1.1/hamcrest-library-1.1.jar"/>
+	<classpathentry kind="var" path="M2_REPO/janino/janino/2.5.15/janino-2.5.15.jar"/>
+	<classpathentry kind="var" path="M2_REPO/org/mortbay/jetty/jetty/6.1.15/jetty-6.1.15.jar" sourcepath="M2_REPO/org/mortbay/jetty/jetty/6.1.15/jetty-6.1.15-sources.jar"/>
+	<classpathentry kind="var" path="M2_REPO/org/mortbay/jetty/jetty-embedded/6.1.15/jetty-embedded-6.1.15.jar"/>
+	<classpathentry kind="var" path="M2_REPO/org/mortbay/jetty/jetty-util/6.1.15/jetty-util-6.1.15.jar"/>
+	<classpathentry kind="var" path="M2_REPO/org/jmock/jmock/2.5.1/jmock-2.5.1.jar"/>
+	<classpathentry kind="var" path="M2_REPO/org/jmock/jmock-legacy/2.5.1/jmock-legacy-2.5.1.jar"/>
+	<classpathentry kind="var" path="M2_REPO/joda-time/joda-time/1.6/joda-time-1.6.jar"/>
+	<classpathentry kind="var" path="M2_REPO/javax/servlet/jsp/jsp-api/2.1/jsp-api-2.1.jar"/>
+	<classpathentry kind="var" path="M2_REPO/junit/junit/4.6/junit-4.6.jar"/>
+	<classpathentry kind="var" path="M2_REPO/org/mvel/mvel2/2.0.12/mvel2-2.0.12.jar"/>
+	<classpathentry kind="var" path="M2_REPO/org/objenesis/objenesis/1.0/objenesis-1.0.jar"/>
+	<classpathentry kind="var" path="M2_REPO/org/mortbay/jetty/servlet-api/2.5-20081211/servlet-api-2.5-20081211.jar"/>
+	<classpathentry kind="var" path="M2_REPO/xpp3/xpp3_min/1.1.4c/xpp3_min-1.1.4c.jar"/>
+	<classpathentry kind="var" path="M2_REPO/com/thoughtworks/xstream/xstream/1.3.1/xstream-1.3.1.jar" sourcepath="M2_REPO/com/thoughtworks/xstream/xstream/1.3.1/xstream-1.3.1-sources.jar"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>

Modified: labs/jbossrules/trunk/drools-compiler/.project
===================================================================
--- labs/jbossrules/trunk/drools-compiler/.project	2009-12-01 20:57:44 UTC (rev 30428)
+++ labs/jbossrules/trunk/drools-compiler/.project	2009-12-02 05:00:53 UTC (rev 30429)
@@ -1,7 +1,10 @@
 <projectDescription>
   <name>drools-compiler</name>
   <comment>A rule production system</comment>
-  <projects/>
+  <projects>
+    <project>drools-api</project>
+    <project>drools-core</project>
+  </projects>
   <buildSpec>
     <buildCommand>
       <name>org.eclipse.jdt.core.javabuilder</name>

Modified: labs/jbossrules/trunk/drools-compiler/src/main/java/org/drools/rule/builder/RuleBuilder.java
===================================================================
--- labs/jbossrules/trunk/drools-compiler/src/main/java/org/drools/rule/builder/RuleBuilder.java	2009-12-01 20:57:44 UTC (rev 30428)
+++ labs/jbossrules/trunk/drools-compiler/src/main/java/org/drools/rule/builder/RuleBuilder.java	2009-12-02 05:00:53 UTC (rev 30429)
@@ -16,6 +16,7 @@
  * limitations under the License.
  */
 
+import java.text.ParseException;
 import java.util.Calendar;
 
 import org.drools.RuntimeDroolsException;
@@ -29,6 +30,10 @@
 import org.drools.rule.Rule;
 import org.drools.spi.Salience;
 import org.drools.time.TimeUtils;
+import org.drools.time.impl.CronExpression;
+import org.drools.time.impl.CronTimer;
+import org.drools.time.impl.IntervalTimer;
+import org.drools.time.impl.Timer;
 import org.drools.util.DateUtils;
 
 /**
@@ -111,10 +116,7 @@
                                                        true ) );
             } else if ( name.equals( "duration" ) ) {
                 String duration = attributeDescr.getValue();
-                if( duration.indexOf( '(' ) >=0 ) {
-                    duration = duration.substring( duration.indexOf( '(' )+1, duration.lastIndexOf( ')' ) );
-                }
-                rule.setDuration( TimeUtils.parseTimeString( duration ) );
+                buildTimer( rule, duration, context);
             } else if ( name.equals( "date-effective" ) ) {
                 final Calendar cal = Calendar.getInstance();
                 cal.setTime( DateUtils.parseDate( attributeDescr.getValue() ) );
@@ -180,5 +182,53 @@
             }
         }
     }
+    
+    private void buildTimer(Rule rule, String timerString, RuleBuildContext context) {
+        if( timerString.indexOf( '(' ) >=0 ) {
+            timerString = timerString.substring( timerString.indexOf( '(' )+1, timerString.lastIndexOf( ')' ) );
+        }
+        
+        int colonPos = timerString.indexOf( ":" );
+        String protocol = null;
+        if ( colonPos == -1 ) {
+            // no protocol so assume interval semantics
+            protocol = "int";
+        } else {
+            protocol = timerString.substring( 0, colonPos );
+        }
+        
+        String body = timerString.substring( colonPos + 1 );
+        
+        Timer timer = null;
+        if ( "cron".equals( protocol ) ) {
+            try {
+                timer = new CronTimer( null, null, new CronExpression( body ) );
+            } catch ( ParseException e ) {
+                context.getErrors().add( "Unable to build set timer '" + timerString + "'");
+                return;
+            }            
+        } else if ( "int".equals( protocol ) ) {
+            String[] times = body.trim().split( "\\s" );
+            long delay = 0;
+            long period = 0;
+            if ( times.length == 1 ) {
+                // only defines a delay
+                delay = TimeUtils.parseTimeString( times[0] );                
+            } else if ( times.length == 2 ) {
+                // defines a delay and a period for intervals
+                delay = TimeUtils.parseTimeString( times[0] );                
+                period = TimeUtils.parseTimeString( times[1] );
+            } else {
+                context.getErrors().add( "Incorrect number of arguments for interval timer '" + timerString + "'");
+                return;
+            }
+            timer = new IntervalTimer(null, null, delay, period);
+            //rule.setDuration( TimeUtils.parseTimeString( duration ) );
+        } else {
+            context.getErrors().add( "Protocol for timer does not exist '" + timerString +"'");
+            return;
+        }
+        rule.setTimer( timer );
+    }
 
 }
\ No newline at end of file

Modified: labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/integrationtests/CepEspTest.java
===================================================================
--- labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/integrationtests/CepEspTest.java	2009-12-01 20:57:44 UTC (rev 30428)
+++ labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/integrationtests/CepEspTest.java	2009-12-02 05:00:53 UTC (rev 30429)
@@ -47,6 +47,7 @@
 import org.drools.runtime.StatefulKnowledgeSession;
 import org.drools.runtime.conf.ClockTypeOption;
 import org.drools.time.SessionPseudoClock;
+import org.drools.time.impl.DurationTimer;
 import org.drools.time.impl.PseudoClockScheduler;
 import org.drools.util.DroolsStreamUtils;
 
@@ -984,7 +985,7 @@
 
         final Rule rule = ruleBase.getPackage( "org.drools" ).getRule( "Delaying Not" );
         assertEquals( 10000,
-                      rule.getDuration().getDuration( null ) );
+                      ((DurationTimer)rule.getTimer()).getDuration() );
 
         SessionConfiguration conf = new SessionConfiguration();
         conf.setClockType( ClockType.PSEUDO_CLOCK );

Modified: labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/integrationtests/ExecutionFlowControlTest.java
===================================================================
--- labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/integrationtests/ExecutionFlowControlTest.java	2009-12-01 20:57:44 UTC (rev 30428)
+++ labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/integrationtests/ExecutionFlowControlTest.java	2009-12-02 05:00:53 UTC (rev 30429)
@@ -4,8 +4,12 @@
 import java.io.InputStreamReader;
 import java.io.Reader;
 import java.io.StringReader;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 import junit.framework.Assert;
 import junit.framework.TestCase;
@@ -13,6 +17,7 @@
 import org.drools.Alarm;
 import org.drools.Cell;
 import org.drools.Cheese;
+import org.drools.ClockType;
 import org.drools.FactHandle;
 import org.drools.KnowledgeBase;
 import org.drools.KnowledgeBaseFactory;
@@ -35,19 +40,29 @@
 import org.drools.compiler.DroolsParserException;
 import org.drools.compiler.PackageBuilder;
 import org.drools.compiler.PackageBuilder.PackageMergeException;
+import org.drools.conf.Option;
 import org.drools.event.ActivationCancelledEvent;
 import org.drools.event.ActivationCreatedEvent;
 import org.drools.event.AgendaEventListener;
 import org.drools.event.DefaultAgendaEventListener;
+import org.drools.integrationtests.eventgenerator.PseudoSessionClock;
 import org.drools.io.ResourceFactory;
 import org.drools.lang.descr.PackageDescr;
 import org.drools.process.instance.ProcessInstance;
 import org.drools.rule.Package;
+import org.drools.runtime.KnowledgeSessionConfiguration;
 import org.drools.runtime.StatefulKnowledgeSession;
+import org.drools.runtime.conf.ClockTypeOption;
 import org.drools.spi.Activation;
 import org.drools.spi.ActivationGroup;
 import org.drools.spi.AgendaGroup;
+import org.drools.time.impl.CronTrigger;
+import org.drools.time.impl.PseudoClockScheduler;
+import org.drools.time.impl.JDKTimerServiceTest.HelloWorldJob;
+import org.drools.time.impl.JDKTimerServiceTest.HelloWorldJobContext;
 
+import sun.net.dns.ResolverConfiguration.Options;
+
 public class ExecutionFlowControlTest extends TestCase {
     protected RuleBase getRuleBase() throws Exception {
 
@@ -843,7 +858,177 @@
         assertEquals( 0,
                       session.getAgenda().getScheduledActivations().length );
     }
+    
+    public void testNoProtocolIntervalTimer() throws Exception {
+        String str = "";
+        str += "package org.simple \n";
+        str += "global java.util.List list \n";
+        str += "rule xxx \n";
+        str += "  duration (30s 10s) ";
+        str += "when \n";
+        str += "then \n";
+        str += "  list.add(\"fired\"); \n";
+        str += "end  \n";
 
+        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
+        kbuilder.add( ResourceFactory.newByteArrayResource( str.getBytes() ),
+                      ResourceType.DRL );
+
+        if ( kbuilder.hasErrors() ) {
+            System.out.println( kbuilder.getErrors() );
+            assertTrue( kbuilder.hasErrors() );
+        }
+
+        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
+        kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );
+
+        KnowledgeSessionConfiguration conf = KnowledgeBaseFactory.newKnowledgeSessionConfiguration();
+        conf.setOption( ClockTypeOption.get( "pseudo" ) );
+        
+        List list = new ArrayList();
+        StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession( conf, null );
+        PseudoClockScheduler timeService = ( PseudoClockScheduler ) ksession.getSessionClock();
+        timeService.advanceTime( new Date().getTime(), TimeUnit.MILLISECONDS );
+        
+        ksession.setGlobal( "list", list );
+        
+        ksession.fireAllRules();        
+        assertEquals( 0, list.size() );
+        
+        timeService.advanceTime( 20, TimeUnit.SECONDS );
+        ksession.fireAllRules();        
+        assertEquals( 0, list.size() );
+        
+        timeService.advanceTime( 15, TimeUnit.SECONDS );
+        ksession.fireAllRules();        
+        assertEquals( 1, list.size() );   
+        
+        timeService.advanceTime( 3, TimeUnit.SECONDS );
+        ksession.fireAllRules();        
+        assertEquals( 1, list.size() );  
+        
+        timeService.advanceTime( 2, TimeUnit.SECONDS );
+        ksession.fireAllRules();        
+        assertEquals( 2, list.size() ); 
+        
+        timeService.advanceTime( 10, TimeUnit.SECONDS );
+        ksession.fireAllRules();        
+        assertEquals( 3, list.size() );                   
+    }
+    
+    public void testIntervalTimer() throws Exception {
+        String str = "";
+        str += "package org.simple \n";
+        str += "global java.util.List list \n";
+        str += "rule xxx \n";
+        str += "  duration (int:30s 10s) ";
+        str += "when \n";
+        str += "then \n";
+        str += "  list.add(\"fired\"); \n";
+        str += "end  \n";
+
+        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
+        kbuilder.add( ResourceFactory.newByteArrayResource( str.getBytes() ),
+                      ResourceType.DRL );
+
+        if ( kbuilder.hasErrors() ) {
+            System.out.println( kbuilder.getErrors() );
+            assertTrue( kbuilder.hasErrors() );
+        }
+
+        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
+        kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );
+
+        KnowledgeSessionConfiguration conf = KnowledgeBaseFactory.newKnowledgeSessionConfiguration();
+        conf.setOption( ClockTypeOption.get( "pseudo" ) );
+        
+        List list = new ArrayList();
+        StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession( conf, null );
+        PseudoClockScheduler timeService = ( PseudoClockScheduler ) ksession.getSessionClock();
+        timeService.advanceTime( new Date().getTime(), TimeUnit.MILLISECONDS );
+        
+        ksession.setGlobal( "list", list );
+        
+        ksession.fireAllRules();        
+        assertEquals( 0, list.size() );
+        
+        timeService.advanceTime( 20, TimeUnit.SECONDS );
+        ksession.fireAllRules();        
+        assertEquals( 0, list.size() );
+        
+        timeService.advanceTime( 15, TimeUnit.SECONDS );
+        ksession.fireAllRules();        
+        assertEquals( 1, list.size() );   
+        
+        timeService.advanceTime( 3, TimeUnit.SECONDS );
+        ksession.fireAllRules();        
+        assertEquals( 1, list.size() );  
+        
+        timeService.advanceTime( 2, TimeUnit.SECONDS );
+        ksession.fireAllRules();        
+        assertEquals( 2, list.size() ); 
+        
+        timeService.advanceTime( 10, TimeUnit.SECONDS );
+        ksession.fireAllRules();        
+        assertEquals( 3, list.size() );                   
+    }    
+    
+    public void testCronTimer() throws Exception {
+        String str = "";
+        str += "package org.simple \n";
+        str += "global java.util.List list \n";
+        str += "rule xxx \n";
+        str += "  duration (cron:15 * * * * ?) ";
+        str += "when \n";
+        str += "then \n";
+        str += "  list.add(\"fired\"); \n";
+        str += "end  \n";
+
+        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
+        kbuilder.add( ResourceFactory.newByteArrayResource( str.getBytes() ),
+                      ResourceType.DRL );
+
+        if ( kbuilder.hasErrors() ) {
+            System.out.println( kbuilder.getErrors() );
+            assertTrue( kbuilder.hasErrors() );
+        }
+
+        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
+        kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );
+
+        KnowledgeSessionConfiguration conf = KnowledgeBaseFactory.newKnowledgeSessionConfiguration();
+        conf.setOption( ClockTypeOption.get( "pseudo" ) );
+        
+        List list = new ArrayList();
+        StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession( conf, null );
+        PseudoClockScheduler timeService = ( PseudoClockScheduler ) ksession.getSessionClock();
+        DateFormat df = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSSZ" );
+        Date date = df.parse( "2009-01-01T00:00:00.000-0000" );
+        
+        timeService.advanceTime( date.getTime(), TimeUnit.MILLISECONDS );
+        
+        ksession.setGlobal( "list", list );
+  
+        ksession.fireAllRules();        
+        assertEquals( 0, list.size() ); 
+                
+        timeService.advanceTime( 10, TimeUnit.SECONDS );
+        ksession.fireAllRules();        
+        assertEquals( 0, list.size() ); 
+                 
+        timeService.advanceTime( 10, TimeUnit.SECONDS );            
+        ksession.fireAllRules();        
+        assertEquals( 1, list.size() ); 
+        
+        timeService.advanceTime( 30, TimeUnit.SECONDS );   
+        ksession.fireAllRules();        
+        assertEquals( 1, list.size() ); 
+        
+        timeService.advanceTime( 30, TimeUnit.SECONDS );  
+        ksession.fireAllRules();        
+        assertEquals( 2, list.size() );                   
+    }        
+
     public void testFireRuleAfterDuration() throws Exception {
         final PackageBuilder builder = new PackageBuilder();
         builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_FireRuleAfterDuration.drl" ) ) );

Modified: labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/rule/builder/dialect/java/RuleBuilderTest.java
===================================================================
--- labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/rule/builder/dialect/java/RuleBuilderTest.java	2009-12-01 20:57:44 UTC (rev 30428)
+++ labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/rule/builder/dialect/java/RuleBuilderTest.java	2009-12-02 05:00:53 UTC (rev 30429)
@@ -50,6 +50,8 @@
 import org.drools.rule.builder.RuleBuildContext;
 import org.drools.rule.builder.RuleBuilder;
 import org.drools.time.TimeUtils;
+import org.drools.time.impl.DurationTimer;
+import org.drools.time.impl.IntervalTimer;
 import org.drools.util.DateUtils;
 import org.jmock.Expectations;
 import org.jmock.Mockery;
@@ -202,7 +204,7 @@
                 oneOf( rule ).setRuleFlowGroup( "mygroup" );
                 oneOf( rule ).setLockOnActive( true );
                 oneOf( rule ).setEnabled( EnabledBoolean.ENABLED_FALSE );
-                oneOf( rule ).setDuration( 60 );
+                oneOf( rule ).setTimer( new IntervalTimer( null , null, TimeUtils.parseTimeString( "60" ), 0 ) );
                 oneOf( rule ).setDateEffective( effective );
                 oneOf( rule ).setDateExpires( expires );
             }
@@ -235,7 +237,7 @@
                 allowing( context ).getRuleDescr(); will( returnValue( ruleDescr ) );
 
                 // expected values for the rule object
-                oneOf( rule ).setDuration( TimeUtils.parseTimeString( "1h30m" ) );
+                oneOf( rule ).setTimer( new IntervalTimer( null , null, TimeUtils.parseTimeString( "1h30m" ), 0 ) );
             }
         } );
 

Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/Scheduler.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/Scheduler.java	2009-12-01 20:57:44 UTC (rev 30428)
+++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/Scheduler.java	2009-12-02 05:00:53 UTC (rev 30429)
@@ -17,7 +17,6 @@
  */
 
 import org.drools.Agenda;
-import org.drools.process.instance.timer.TimerManager.TimerTrigger;
 import org.drools.time.Job;
 import org.drools.time.JobContext;
 import org.drools.time.JobHandle;
@@ -48,12 +47,11 @@
      *            The working memory session.
      */
     public static void scheduleAgendaItem(final ScheduledAgendaItem item, InternalAgenda agenda) {
-        DuractionJob job = new DuractionJob();        
-        DuractionJobContext ctx = new DuractionJobContext( item, agenda );
-        Trigger trigger = new PointInTimeTrigger( item.getRule().getDuration().getDuration( item.getTuple() ) +
-                                                       ((InternalWorkingMemory)agenda.getWorkingMemory()).getTimerService().getCurrentTime());
+        Trigger trigger = item.getRule().getTimer().createTrigger( ((InternalWorkingMemory)agenda.getWorkingMemory()).getTimerService().getCurrentTime() );
         
-        
+        ActivationTimerJob job = new ActivationTimerJob();        
+        ActivationTimerJobContext ctx = new ActivationTimerJobContext( trigger, item, agenda );        
+                
         JobHandle jobHandle = ((InternalWorkingMemory)agenda.getWorkingMemory()).getTimerService().scheduleJob( job, ctx, trigger );
         item.setJobHandle( jobHandle );
     }
@@ -62,31 +60,32 @@
         ((InternalWorkingMemory)agenda.getWorkingMemory()).getTimerService().removeJob( item.getJobHandle() );
     }    
     
-    public static class DuractionJob implements Job {
+    public static class ActivationTimerJob implements Job {
         public void execute(JobContext ctx) {
-            InternalAgenda agenda = ( InternalAgenda ) ((DuractionJobContext)ctx).getAgenda();
-            ScheduledAgendaItem item  = ((DuractionJobContext)ctx).getScheduledAgendaItem();
+            InternalAgenda agenda = ( InternalAgenda ) ((ActivationTimerJobContext)ctx).getAgenda();
+            ScheduledAgendaItem item  = ((ActivationTimerJobContext)ctx).getScheduledAgendaItem();
             
             agenda.fireActivation( item );
-            agenda.getScheduledActivationsLinkedList().remove( item );
+            if ( ((ActivationTimerJobContext)ctx).getTrigger().hasNextFireTime() == null ) {
+                agenda.getScheduledActivationsLinkedList().remove( item );
+            }
             agenda.getWorkingMemory().fireAllRules();            
         }        
     }
     
-    public static class DuractionJobContext implements JobContext {
+    public static class ActivationTimerJobContext implements JobContext {
         private JobHandle jobHandle;
         private ScheduledAgendaItem scheduledAgendaItem;
-        private Agenda agenda;                
+        private Agenda agenda;          
+        private Trigger trigger;
         
-        public DuractionJobContext(ScheduledAgendaItem scheduledAgendaItem,
+        public ActivationTimerJobContext(Trigger trigger,
+                                         ScheduledAgendaItem scheduledAgendaItem,
                                    Agenda agenda) {
+            this.trigger = trigger;
             this.scheduledAgendaItem = scheduledAgendaItem;
             this.agenda = agenda;
         }
-
-        public DuractionJobContext(ScheduledAgendaItem scheduledAgendaItem) {
-            this.scheduledAgendaItem = scheduledAgendaItem;
-        }
         
         public Agenda getAgenda() {
             return this.agenda;
@@ -102,6 +101,10 @@
 
         public void setJobHandle(JobHandle jobHandle) {
             this.jobHandle = jobHandle;
+        } 
+        
+        public Trigger getTrigger() {
+            return trigger;
         }        
     }    
 }

Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/process/instance/timer/TimerManager.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/process/instance/timer/TimerManager.java	2009-12-01 20:57:44 UTC (rev 30428)
+++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/process/instance/timer/TimerManager.java	2009-12-02 05:00:53 UTC (rev 30429)
@@ -1,8 +1,5 @@
 package org.drools.process.instance.timer;
 
-import java.io.IOException;
-import java.io.ObjectInput;
-import java.io.ObjectOutput;
 import java.util.Collection;
 import java.util.Date;
 import java.util.HashMap;
@@ -14,7 +11,7 @@
 import org.drools.time.JobContext;
 import org.drools.time.JobHandle;
 import org.drools.time.TimerService;
-import org.drools.time.Trigger;
+import org.drools.time.impl.IntervalTrigger;
 
 /**
  * 
@@ -45,7 +42,7 @@
 
         JobHandle jobHandle = this.timerService.scheduleJob( processJob,
                                                              ctx,
-                                                             new TimerTrigger( timerService.getCurrentTime(),
+                                                             new IntervalTrigger( timerService.getCurrentTime(),
                                                                                timer.getDelay(),
                                                                                timer.getPeriod() ) );
         timer.setJobHandle( jobHandle );
@@ -75,7 +72,7 @@
 			}
 		}
 		JobHandle jobHandle = this.timerService.scheduleJob(
-			processJob, ctx, new TimerTrigger(timerService.getCurrentTime(),
+			processJob, ctx, new IntervalTrigger(timerService.getCurrentTime(),
                                               delay, 
                                               timer.getPeriod()));
 		timer.setJobHandle(jobHandle);
@@ -139,51 +136,6 @@
 
     }
 
-    public static class TimerTrigger
-        implements
-        Trigger {
-        private Date next;
-        private long period;
-
-        public TimerTrigger() {
-            
-        }
-        
-        public TimerTrigger(long currentTS,
-                            long delay,
-                            long period) {
-            this.next = new Date( currentTS + delay  );
-            this.period = period;
-        }
-
-        public Date hasNextFireTime() {
-            return next;
-        }
-        
-        public Date nextFireTime() {
-            Date date = next;
-            if ( this.period != 0 ) {
-                // repeated fires for the given period
-                // FIXME: this is not safe for serialization
-                next = new Date( next.getTime() + this.period );                
-            } else {
-                next = null;
-            }
-            return date;
-        }
-
-        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
-            this.next = (Date) in.readObject();
-            this.period = in.readLong();
-        }
-
-        public void writeExternal(ObjectOutput out) throws IOException {
-            out.writeObject( this.next );
-            out.writeLong( this.period );
-        }
-
-    }
-
     public static class ProcessJobContext
         implements
         JobContext {

Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/RuleTerminalNode.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/RuleTerminalNode.java	2009-12-01 20:57:44 UTC (rev 30428)
+++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/RuleTerminalNode.java	2009-12-02 05:00:53 UTC (rev 30429)
@@ -39,6 +39,7 @@
 import org.drools.spi.Activation;
 import org.drools.spi.Duration;
 import org.drools.spi.PropagationContext;
+import org.drools.time.impl.Timer;
 import org.drools.util.Iterator;
 import org.drools.util.LeftTupleList;
 
@@ -200,10 +201,10 @@
         }
 
         final InternalAgenda agenda = (InternalAgenda) workingMemory.getAgenda();
+        
+        final Timer timer = this.rule.getTimer();
 
-        final Duration dur = this.rule.getDuration();
-
-        if ( dur != null && dur.getDuration( tuple ) > 0 ) {
+        if ( timer != null ) {
             final ScheduledAgendaItem item = agenda.createScheduledAgendaItem( tuple,
                                                                                context,
                                                                                this.rule,

Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/builder/PatternBuilder.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/builder/PatternBuilder.java	2009-12-01 20:57:44 UTC (rev 30428)
+++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/builder/PatternBuilder.java	2009-12-02 05:00:53 UTC (rev 30429)
@@ -48,6 +48,9 @@
 import org.drools.spi.Constraint;
 import org.drools.spi.Duration;
 import org.drools.spi.ObjectType;
+import org.drools.time.impl.CompositeMaxDurationTimer;
+import org.drools.time.impl.DurationTimer;
+import org.drools.time.impl.Timer;
 
 /**
  * A builder for patterns
@@ -181,18 +184,32 @@
             Declaration target = constraint.getRequiredDeclarations()[0];
             if( target.isPatternDeclaration() && target.getPattern().getObjectType().isEvent() ) {
                 long uplimit = ((VariableConstraint) constraint).getInterval().getUpperBound();
-                Duration dur = context.getRule().getDuration();
-                Duration newDur = new FixedDuration( uplimit ); 
-                if( dur instanceof CompositeMaxDuration ) {
-                    ((CompositeMaxDuration)dur).addDuration( newDur );
+                
+                Timer timer = context.getRule().getTimer();                
+                DurationTimer durationTimer = new DurationTimer(uplimit);                
+                
+                if ( timer instanceof CompositeMaxDurationTimer ) {
+                    // already a composite so just add
+                    ((CompositeMaxDurationTimer)timer).addDurationTimer( durationTimer );
                 } else {
-                    if( dur == null ) {
-                        dur = newDur;
+                    if ( timer == null ) {
+                        // no timer exists, so ok on it's own
+                        timer = durationTimer;
                     } else {
-                        dur = new CompositeMaxDuration( dur );
-                        ((CompositeMaxDuration)dur).addDuration( newDur );
+                        // timer exists so we need to make a composite
+                        timer = new CompositeMaxDurationTimer();
+                        if ( timer instanceof DurationTimer ) {
+                            // previous timer was a duration, so add another DurationTimer
+                            ((CompositeMaxDurationTimer)timer).addDurationTimer( ( DurationTimer ) timer );                            
+                        } else {
+                            // previous timer was not a duration, so set it as the delegate Timer.
+                            ((CompositeMaxDurationTimer)timer).setTimer( context.getRule().getTimer() );    
+                        }
+                        // now add the new durationTimer
+                        ((CompositeMaxDurationTimer)timer).addDurationTimer( durationTimer );
                     }
-                    context.getRule().setDuration( dur );
+                    // with the composite made, reset it on the Rule
+                    context.getRule().setTimer( timer );
                 }
             }
         }

Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/rule/Rule.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/rule/Rule.java	2009-12-01 20:57:44 UTC (rev 30428)
+++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/rule/Rule.java	2009-12-02 05:00:53 UTC (rev 30429)
@@ -38,6 +38,7 @@
 import org.drools.spi.Salience;
 import org.drools.spi.Tuple;
 import org.drools.spi.Wireable;
+import org.drools.time.impl.Timer;
 
 /**
  * A <code>Rule</code> contains a set of <code>Test</code>s and a
@@ -93,10 +94,10 @@
 
     /** Consequence. */
     private Consequence       consequence;
+    
+    /** Timer semantics that controls the firing of a rule */
+    private Timer             timer;
 
-    /** Truthness duration. */
-    private Duration          duration;
-
     /** Load order in Package */
     private long              loadOrder;
 
@@ -143,7 +144,7 @@
         } else {
             out.writeObject(this.consequence);   
         } 
-        out.writeObject(duration);
+        out.writeObject(timer);
         out.writeLong(loadOrder);
         out.writeBoolean(noLoop);
         out.writeBoolean(autoFocus);
@@ -173,7 +174,7 @@
         metaAttributes = (Map<String,String>)in.readObject();
         
         consequence = (Consequence)in.readObject();
-        duration = (Duration)in.readObject();
+        timer = (Timer)in.readObject();
         loadOrder   = in.readLong();
         noLoop = in.readBoolean();
         autoFocus = in.readBoolean();
@@ -255,48 +256,24 @@
     }
 
     /**
-     * Set the truthness duration. This causes a delay before the firing of the
-     * <code>Consequence</code> if the rule is still true at the end of the
-     * duration.
-     *
-     * <p>
-     * This is merely a convenience method for calling
-     * {@link #setDuration(Duration)}with a <code>FixedDuration</code>.
-     * </p>
-     *
-     * @see #setDuration(Duration)
-     * @see FixedDuration
-     *
-     * @param seconds -
-     *            The number of seconds the rule must hold true in order to
-     *            fire.
+     * Returns the Timer semantics for a rule. Timer based rules are not added directly to the Agenda
+     * instead they are scheduled for Agenda addition, based on the timer.
+     * @return
      */
-    public void setDuration(final long ms) {
-        this.duration = new FixedDuration( ms );
+    public Timer getTimer() {
+        return timer;
     }
 
     /**
-     * Set the truthness duration object. This causes a delay before the firing
-     * of the <code>Consequence</code> if the rule is still true at the end of
-     * the duration.
-     *
-     * @param duration
-     *            The truth duration object.
+     * Sets the timer semantics for a rule. Timer based rules are not added directly to the Agenda
+     * instead they are scheduled for Agenda addition, based on the timer.
+     * @param timer
      */
-    public void setDuration(final Duration duration) {
-        this.duration = duration;
+    public void setTimer(Timer timer) {
+        this.timer = timer;
     }
 
     /**
-     * Retrieve the truthness duration object.
-     *
-     * @return The truthness duration object.
-     */
-    public Duration getDuration() {
-        return this.duration;
-    }
-
-    /**
      * Determine if this rule is internally consistent and valid.
      * This will include checks to make sure the rules semantic components (actions and predicates)
      * are valid.

Added: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/CompositeMaxDurationTimer.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/CompositeMaxDurationTimer.java	                        (rev 0)
+++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/CompositeMaxDurationTimer.java	2009-12-02 05:00:53 UTC (rev 30429)
@@ -0,0 +1,51 @@
+package org.drools.time.impl;
+
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.drools.time.Trigger;
+
+/**
+ * While a rule might have multiple DurationTimers, due to LHS CEP rules, there can only ever
+ * be one timer attribute. Duration rules should be considered a priority over the one timer rule.
+ * So the Timer cannot fire, until the maximum duration has passed.
+ *
+ */
+public class CompositeMaxDurationTimer
+    implements
+    Timer {
+
+    private List<DurationTimer> durations;
+
+    private Timer               timer;
+
+    public CompositeMaxDurationTimer() {
+
+    }
+
+    public void addDurationTimer(final DurationTimer durationTimer) {
+        if ( this.durations == null ) {
+            this.durations = new LinkedList<DurationTimer>();
+        }
+        this.durations.add( durationTimer );
+    }
+
+    public void setTimer(Timer timer) {
+        this.timer = timer;
+    }
+
+    public Trigger createTrigger(long timestamp) {
+        return new CompositeMaxDurationTrigger( new Date( getMaxDuration() + timestamp ),
+                                                timer.createTrigger( timestamp ) );
+    }
+
+    private long getMaxDuration() {
+        long result = 0;
+        for ( DurationTimer durationTimer : durations ) {
+            result = Math.max( result,
+                               durationTimer.getDuration() );
+        }
+        return result;
+    }
+}

Added: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/CompositeMaxDurationTrigger.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/CompositeMaxDurationTrigger.java	                        (rev 0)
+++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/CompositeMaxDurationTrigger.java	2009-12-02 05:00:53 UTC (rev 30429)
@@ -0,0 +1,37 @@
+package org.drools.time.impl;
+
+import java.util.Date;
+
+import org.drools.time.Trigger;
+
+public class CompositeMaxDurationTrigger
+    implements
+    Trigger {
+    private Trigger            trigger;
+    private Date               timestamp;
+
+    public CompositeMaxDurationTrigger(Date timestamp, // this is the first duration that takes priority
+                                       Trigger trigger) {
+        this.timestamp = timestamp;
+        this.trigger = trigger;
+    }
+
+    public Date hasNextFireTime() {
+        if ( this.timestamp != null ) {
+            return this.timestamp;
+        } else {
+            return this.trigger.hasNextFireTime();
+        }
+    }
+
+    public Date nextFireTime() {
+        if ( this.timestamp != null ) {
+            Date next = this.timestamp;
+            this.timestamp = null;
+            return next;
+        } else {
+            return trigger.nextFireTime();
+        }        
+    }
+
+}

Added: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/CronTimer.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/CronTimer.java	                        (rev 0)
+++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/CronTimer.java	2009-12-02 05:00:53 UTC (rev 30429)
@@ -0,0 +1,98 @@
+package org.drools.time.impl;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.text.ParseException;
+import java.util.Date;
+
+import org.drools.time.Trigger;
+
+public class CronTimer
+    implements
+    Timer,
+    Externalizable {
+    private Date           startTime;
+    private Date           endTime;
+    private CronExpression cronExpression;
+    
+    public CronTimer() {
+        
+    }
+
+    public CronTimer(Date startTime,
+                     Date endTime,
+                     CronExpression cronExpression) {
+        this.startTime = startTime;
+        this.endTime = endTime;
+        this.cronExpression = cronExpression;
+    }
+
+    public void writeExternal(ObjectOutput out) throws IOException {
+        out.writeObject( startTime );
+        out.writeObject( endTime );
+        out.writeObject( cronExpression.getCronExpression() );
+    }
+
+    public void readExternal(ObjectInput in) throws IOException,
+                                            ClassNotFoundException {
+        this.startTime = (Date) in.readObject();
+        this.endTime = (Date) in.readObject();
+        String string = (String) in.readObject();
+        try {
+            this.cronExpression = new CronExpression( string );
+        } catch ( ParseException e ) {
+            throw new RuntimeException( "Unable to marshal CronExpression '" + string + "'",
+                                        e );
+        }
+    }
+
+    public Date getStartTime() {
+        return startTime;
+    }
+
+    public Date getEndTime() {
+        return endTime;
+    }
+
+    public CronExpression getCronExpression() {
+        return cronExpression;
+    }
+
+    public Trigger createTrigger(long timestamp) {
+        return new CronTrigger( timestamp,
+                                this.startTime,
+                                this.endTime,
+                                this.cronExpression );
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((cronExpression.getCronExpression() == null) ? 0 : cronExpression.getCronExpression().hashCode());
+        result = prime * result + ((endTime == null) ? 0 : endTime.hashCode());
+        result = prime * result + ((startTime == null) ? 0 : startTime.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if ( this == obj ) return true;
+        if ( obj == null ) return false;
+        if ( getClass() != obj.getClass() ) return false;
+        CronTimer other = (CronTimer) obj;
+        if ( cronExpression.getCronExpression() == null ) {
+            if ( other.cronExpression.getCronExpression() != null ) return false;
+        } else if ( !cronExpression.getCronExpression().equals( other.cronExpression.getCronExpression() ) ) return false;
+        if ( endTime == null ) {
+            if ( other.endTime != null ) return false;
+        } else if ( !endTime.equals( other.endTime ) ) return false;
+        if ( startTime == null ) {
+            if ( other.startTime != null ) return false;
+        } else if ( !startTime.equals( other.startTime ) ) return false;
+        return true;
+    }
+
+}

Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/CronTrigger.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/CronTrigger.java	2009-12-01 20:57:44 UTC (rev 30428)
+++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/CronTrigger.java	2009-12-02 05:00:53 UTC (rev 30429)
@@ -18,7 +18,12 @@
     private Date previousFireTime = null;
     private transient TimeZone timeZone = null;   
     
+    
     public CronTrigger(long timestamp, Date startTime, Date endTime, String cronExpression) {
+        this( timestamp, startTime, endTime, determineCronExpression( cronExpression ));
+    }
+    
+    public CronTrigger(long timestamp, Date startTime, Date endTime, CronExpression cronExpression) {
         setCronExpression(cronExpression);
 
         if (startTime == null) {
@@ -30,7 +35,7 @@
         }
         setTimeZone(TimeZone.getDefault());        
         
-        this.nextFireTime = getFireTimeAfter(new Date(getStartTime().getTime() - 1000l));
+        this.nextFireTime = getFireTimeAfter(new Date(getStartTime().getTime() - 1000l));        
     }
     
     public Date getStartTime() {
@@ -172,14 +177,22 @@
     }
     
     public void setCronExpression(String cronExpression) {
-        try {
+        setCronExpression( determineCronExpression(cronExpression) );
+    }    
+    
+    public void setCronExpression(CronExpression cronExpression) {
         TimeZone origTz = getTimeZone();
-        this.cronEx = new CronExpression(cronExpression);
+        this.cronEx = cronExpression;
         this.cronEx.setTimeZone(origTz);
+    }  
+    
+    public static CronExpression determineCronExpression(String cronExpression) {
+        try {
+            return new CronExpression(cronExpression);
         } catch ( Exception e ) {
             throw new RuntimeException("Unable to parse cron expression '" + cronExpression + "'", e);
-        }
-    }    
+        }        
+    }
 
     public Date hasNextFireTime() {
         return this.nextFireTime;

Added: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/DurationTimer.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/DurationTimer.java	                        (rev 0)
+++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/DurationTimer.java	2009-12-02 05:00:53 UTC (rev 30429)
@@ -0,0 +1,59 @@
+package org.drools.time.impl;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+import org.drools.time.Trigger;
+
+public class DurationTimer
+    implements
+    Timer,
+    Externalizable {
+    private long duration;
+    
+    public DurationTimer() {
+        
+    }
+    
+    public DurationTimer(long duration) {
+        this.duration = duration;
+    }    
+
+    public void writeExternal(ObjectOutput out) throws IOException {
+        out.writeLong( duration );
+    }
+
+    public void readExternal(ObjectInput in) throws IOException,
+                                            ClassNotFoundException {
+        duration = in.readLong();
+    }
+
+    public long getDuration() {
+        return duration;
+    }
+
+    public Trigger createTrigger(long timestamp) {
+        return new PointInTimeTrigger( timestamp + duration );
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + (int) (duration ^ (duration >>> 32));
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if ( this == obj ) return true;
+        if ( obj == null ) return false;
+        if ( getClass() != obj.getClass() ) return false;
+        DurationTimer other = (DurationTimer) obj;
+        if ( duration != other.duration ) return false;
+        return true;
+    }
+   
+}

Added: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/IntervalTimer.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/IntervalTimer.java	                        (rev 0)
+++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/IntervalTimer.java	2009-12-02 05:00:53 UTC (rev 30429)
@@ -0,0 +1,98 @@
+package org.drools.time.impl;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.Date;
+
+import org.drools.time.Trigger;
+
+public class IntervalTimer
+    implements
+    Timer,
+    Externalizable {
+    private Date startTime;
+    private Date endTime;
+    private long delay;
+    private long period;
+    
+    public IntervalTimer() {
+        
+    }
+
+    public IntervalTimer(Date startTime,
+                         Date endTime,
+                         long delay,
+                         long period) {
+        this.startTime = startTime;
+        this.endTime = endTime;
+        this.delay = delay;
+        this.period = period;
+    }
+
+    public void writeExternal(ObjectOutput out) throws IOException {
+        out.writeObject( startTime );
+        out.writeObject( endTime );
+        out.writeLong( delay );
+        out.writeLong( period );
+    }
+
+    public void readExternal(ObjectInput in) throws IOException,
+                                            ClassNotFoundException {
+        this.startTime = (Date) in.readObject();
+        this.endTime = (Date) in.readObject();
+        this.delay = in.readLong();
+        this.period = in.readLong();
+    }
+
+    public Date getStartTime() {
+        return startTime;
+    }
+
+    public Date getEndTime() {
+        return endTime;
+    }
+
+    public long getDelay() {
+        return delay;
+    }
+
+    public long getPeriod() {
+        return period;
+    }
+
+    public Trigger createTrigger(long timestamp) {
+        return new IntervalTrigger( timestamp,
+                                    delay,
+                                    period );
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + (int) (delay ^ (delay >>> 32));
+        result = prime * result + ((endTime == null) ? 0 : endTime.hashCode());
+        result = prime * result + (int) (period ^ (period >>> 32));
+        result = prime * result + ((startTime == null) ? 0 : startTime.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if ( this == obj ) return true;
+        if ( obj == null ) return false;
+        if ( getClass() != obj.getClass() ) return false;
+        IntervalTimer other = (IntervalTimer) obj;
+        if ( delay != other.delay ) return false;
+        if ( endTime == null ) {
+            if ( other.endTime != null ) return false;
+        } else if ( !endTime.equals( other.endTime ) ) return false;
+        if ( period != other.period ) return false;
+        if ( startTime == null ) {
+            if ( other.startTime != null ) return false;
+        } else if ( !startTime.equals( other.startTime ) ) return false;
+        return true;
+    }
+}

Added: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/IntervalTrigger.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/IntervalTrigger.java	                        (rev 0)
+++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/IntervalTrigger.java	2009-12-02 05:00:53 UTC (rev 30429)
@@ -0,0 +1,57 @@
+/**
+ * 
+ */
+package org.drools.time.impl;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.Date;
+
+import org.drools.time.Trigger;
+
+public class IntervalTrigger
+    implements
+    Trigger {
+    private Date next;
+    private long period;
+
+    public IntervalTrigger() {
+
+    }
+
+    public IntervalTrigger(long currentTS,
+                           long delay,
+                           long period) {
+        this.next = new Date( currentTS + delay );
+        this.period = period;
+    }
+
+    public Date hasNextFireTime() {
+        return next;
+    }
+
+    public Date nextFireTime() {
+        Date date = next;
+        if ( this.period != 0 ) {
+            // repeated fires for the given period
+            // FIXME: this is not safe for serialization
+            next = new Date( next.getTime() + this.period );
+        } else {
+            next = null;
+        }
+        return date;
+    }
+
+    public void readExternal(ObjectInput in) throws IOException,
+                                            ClassNotFoundException {
+        this.next = (Date) in.readObject();
+        this.period = in.readLong();
+    }
+
+    public void writeExternal(ObjectOutput out) throws IOException {
+        out.writeObject( this.next );
+        out.writeLong( this.period );
+    }
+
+}
\ No newline at end of file

Added: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/Timer.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/Timer.java	                        (rev 0)
+++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/time/impl/Timer.java	2009-12-02 05:00:53 UTC (rev 30429)
@@ -0,0 +1,7 @@
+package org.drools.time.impl;
+
+import org.drools.time.Trigger;
+
+public interface Timer {
+    Trigger createTrigger(long timestamp);
+}

Modified: labs/jbossrules/trunk/drools-core/src/test/java/org/drools/reteoo/AgendaTest.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/test/java/org/drools/reteoo/AgendaTest.java	2009-12-01 20:57:44 UTC (rev 30428)
+++ labs/jbossrules/trunk/drools-core/src/test/java/org/drools/reteoo/AgendaTest.java	2009-12-02 05:00:53 UTC (rev 30429)
@@ -57,6 +57,7 @@
 import org.drools.spi.PropagationContext;
 import org.drools.spi.RuleFlowGroup;
 import org.drools.spi.Salience;
+import org.drools.time.impl.DurationTimer;
 
 /**
  * @author mproctor
@@ -153,7 +154,7 @@
                       agenda.getFocus().size() );
 
         rule1.setNoLoop( false );
-        rule2.setDuration( 5000 );
+        rule2.setTimer( new DurationTimer( 5000 ) );
 
         node1.assertLeftTuple( tuple,
                                context1,

Modified: labs/jbossrules/trunk/drools-core/src/test/java/org/drools/reteoo/SchedulerTest.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/test/java/org/drools/reteoo/SchedulerTest.java	2009-12-01 20:57:44 UTC (rev 30428)
+++ labs/jbossrules/trunk/drools-core/src/test/java/org/drools/reteoo/SchedulerTest.java	2009-12-02 05:00:53 UTC (rev 30429)
@@ -36,6 +36,7 @@
 import org.drools.spi.KnowledgeHelper;
 import org.drools.spi.PropagationContext;
 import org.drools.spi.Tuple;
+import org.drools.time.impl.DurationTimer;
 
 /**
  * @author mproctor
@@ -83,20 +84,8 @@
             }
         } );
 
-        /* 1/10th of a second */
-        final Duration duration = new Duration() {
-            /**
-             *
-             */
-            private static final long serialVersionUID = 400L;
+        rule.setTimer( new DurationTimer(100) );
 
-            public long getDuration(Tuple tuple) {
-                return 100;
-            }
-
-        };
-        rule.setDuration( duration );
-
         final PropagationContext context = new PropagationContextImpl( 0,
                                                                        PropagationContext.ASSERTION,
                                                                        null,
@@ -136,20 +125,6 @@
                                                             buildContext );
         final List data = new ArrayList();
 
-        /* 1/10th of a second */
-        final Duration duration = new Duration() {
-            /**
-             *
-             */
-            private static final long serialVersionUID = 400L;
-
-            public long getDuration(Tuple tuple) {
-                return 100;
-            }
-
-        };
-
-
         // add consequence
         rule.setConsequence( new org.drools.spi.Consequence() {
             /**
@@ -193,7 +168,7 @@
         final LeftTuple tuple1 = new LeftTuple( new DefaultFactHandle( 1,
                                                                        "cheese" ), null,
                                                                        true  );
-        rule.setDuration( duration );
+        rule.setTimer( new DurationTimer(100) );
 
         node.assertLeftTuple( tuple1,
                           context1,
@@ -227,20 +202,7 @@
                                                             rule.getLhs(),
                                                             buildContext );
 
-        /* 1/10th of a second */
-        final Duration duration = new Duration() {
-            /**
-             *
-             */
-            private static final long serialVersionUID = 400L;
-
-            public long getDuration(Tuple tuple) {
-                return 100;
-            }
-
-        };
-
-        rule.setDuration( duration );
+        rule.setTimer( new DurationTimer(100) );
         rule.setNoLoop( true );
 
         // add consequence



More information about the jboss-svn-commits mailing list