Digest The Tortoies And The Hare

== The Tortoies And The Hare ==
I was looking for a API that could help me create a data driven model for a application I was writting at work. Since it was a Java application I turned my eyes towards [http://jakarta.apache.org Jakarta] (a true saviour for us Java coders) and found [http://jakarta.apache.org/commons/digester/ Jakarta Digester]. It seemed to full fill all that I needed. So I took it for a spin.

While pondering what to implement I remembered the old fable [[wikipedia:The_Tortoise_and_the_Hare|The Tortoise and the Hare]] and it was clear to me I needed to create a simulation of this historic race.

== What is Digester ==
The [http://jakarta.apache.org/commons/digester/ Digester] project describes it self as:

Basically, the Digester package lets you configure an XML -> Java object mapping module, which triggers 
certain actions called rules whenever a particular pattern of nested XML elements is recognized. A rich set 
of predefined rules is available for your use, or you can also create your own.

In my own words I would say that [http://jakarta.apache.org/commons/digester/ Digester] is a API that allows me to build a structure of [[wikipedia:POJO|POJO]]:s based on content in a XML file. The mapping from XML elements to Java objects is controlled using a rule file.

It's a API that provides the data driven capabitites without adding alot of more stuff that I just don't need at the moment.

== First implementation ==
Well lets get on with it. A fast OOA gives me two main classes: The race (DigestRace) and the participants (Contender). The race has one or more participants and a distance that the participants must run.
The participant is responsible for telling the race (judges?) when he/she has finished and the race is not over until all participants has reached goal.

=== Defining the race ===
So the race would look something like this if we described it in XML. The race is 400 meters long and has two contenders. The Tortoise who is slow but steady and with a velocity of 10 m/second (ok it's a bit fast for a tortoise but it's just code OK). And the Hare, fast but a wee bit lazy and with a velocity of 40 m/second.

File: thegrandrace.xml

<?xml version="1.0"?>
<race>
  <distance>400</distance>
  <contender name="Mr Tortoise" type="SlowAndSteady">
    <velocity>10</velocity>
  </contender>
  <contender name="Mr Hare" type="FastAndLazy">
    <velocity>40</velocity>
  </contender>
</race>

=== Implementing the Java Classes ===
The race logic is implemented using Java classes. The main logic for the race (and the judges) are implemented in the DigestRace class and the logic for the contenders are implemented in the Contender class.

File: DigestRace.java

package com.ullgren.pontus.test.digestrace;
 
import java.io.File;
import java.util.ArrayList;
 
import org.apache.commons.digester.Digester;
import org.apache.commons.digester.xmlrules.DigesterLoader;
 
public class DigestRace {
 
        private long distance;
        private long noContendersFinished;
        private ArrayList<Thread> contenders;
        
        /**
         * @param args
         */
        public static void main(String[] args) {
                try {
                        File input = new File( "thegrandrace.xml" );
                File rules = new File( "first-race-rules.xml" );
        
                Digester digester = DigesterLoader.createDigester( rules.toURL() );
        
                DigestRace theRace = (DigestRace)digester.parse( input );
                System.out.println("Lets start the first race! The distance is " + theRace.getDistance());
                theRace.go();
                theRace.waitForAllContendersToFinish();
                System.out.println("Race Finished");
                }
                catch (Exception e) {
                        e.printStackTrace();
                }        
        }
        
        private void waitForAllContendersToFinish() {
                for(Thread contender: this.contenders) {
                        if (contender.isAlive()) {
                                try {
                                        contender.join();
                                } catch (InterruptedException e) {
                                }
                        }
                }
        }
 
        private void go() {
                for(Thread starter: this.contenders) {
                        starter.start();
                }
                
        }
 
        public DigestRace() {
                this.distance = 1000;
                this.noContendersFinished = 0;
                this.contenders = new ArrayList<Thread>();
        }
        
        synchronized public void reportFinished(Contender contender) {
                this.noContendersFinished++;
                System.out.println(contender.getName() + " finished. Position: " + this.noContendersFinished );
        }
        
        public void addContender(Contender contender) {
                contender.setRace(this);
                this.contenders.add(new Thread(contender));
        }
 
        public long getDistance() {
                return distance;
        }
 
        public void setDistance(long distance) {
                this.distance = distance;
        }
 
        public void setDistance(String distance) {
                this.distance = Long.valueOf(distance);
        }
 
}

}}

File: Contender.java

package com.ullgren.pontus.test.digestrace;
 
public class Contender implements Runnable {
 
        private float velocity;
        private DigestRace race;
        private String name;
        
        public void run() {
                // 
                float meters_traveled = 0;
                while(race.getDistance() >= meters_traveled) {
                        try {
                                Thread.sleep(1000);
                        } catch (InterruptedException e) {
                                e.printStackTrace();
                        }
                        meters_traveled += this.velocity;
                }
                this.race.reportFinished(this);
        }
 
        public DigestRace getRace() {
                return race;
        }
 
        public void setRace(DigestRace race) {
                this.race = race;
        }
 
        public float getVelocity() {
                return velocity;
        }
 
        public void setVelocity(float velocity) {
                this.velocity = velocity;
        }
        
        public void setVelocity(String velocity) {
                this.velocity = Float.valueOf(velocity);
        }
 
        public String getName() {
                return name;
        }
 
        public void setName(String name) {
                this.name = name;
        }
 
}

=== The rules ===
We want to map the XML element into instances of the implemented Java classes.
The race element should be mapped to a DigestRace object and the race contenders should be mapped to objects of the type Contender.

Attributes and sub-elements of both race and contender is also mapped into the class fields (using the set-methods).

With Digester this is done using a rule file which defines the mapping. Below is a simple rule file that makes the nessecary mappings.

File: first-race-rules.xml

<?xml version="1.0"?>
<digester-rules>
   <pattern value="race">
      <object-create-rule classname="com.ullgren.pontus.test.digestrace.DigestRace" />
      <call-method-rule pattern="distance" methodname="setDistance" paramcount="0" />
   </pattern>
 
 
   <pattern value="race/contender">
      <object-create-rule classname="com.ullgren.pontus.test.digestrace.Contender" />
      <set-properties-rule>
         <alias attr-name="name" prop-name="name" />
      </set-properties-rule>   
      <call-method-rule pattern="velocity" methodname="setVelocity"
                        paramcount="0" />
      <call-method-rule pattern="title" methodname="setTitle" 
                        paramcount="0" />
      <set-next-rule methodname="addContender" />
   </pattern>
</digester-rules>

=== The result ===
Running the application gives the following result.

Lets start the first race! The distance is 44444
Mr Hare finished. Position: 1
Mr Tortoise finished. Position: 2
Race Finished

'''Wait a minute''' ... the moral of the story should be that the tortoise came in first since the Hare took a nap instead of concentrating on the race. But in this case the Hare won.

== Fixing the moral ==
To get the same moral as the original story we need to do the implementation so that the "FastAndLazy" contenders takes a nap from time to time.
One way to achiove this would be to add the type attribute as a field in the Contender class and to a if-statement in the run method of the Contender class. Allthough this would give us a perfectly working solution, I want to create another solution. One that lets us create sub-classes of the Contender class and override the run method behaviour in these sub-classes.

=== A new Java class ... ===
So lets create a new Java class for the Fast-And-Lazy contenders.
File: FastAndLazyContender.java

package com.ullgren.pontus.test.digestrace;
 
public class FastAndLazyContender extends Contender {
        public void run() {
                float meters_traveled = 0;
                while(this.getRace().getDistance() >= meters_traveled) {
                        try {
                                Thread.sleep(1000);
                        } catch (InterruptedException e) {
                                e.printStackTrace();
                        }
                        meters_traveled += this.getVelocity();
                        // Every second it's a 50 % chance that the 
                        // lazy contender takes a nap.
                        if ( Math.random() < 0.5 ) {
                                try {
                                        System.out.println(this.getName() + " takes a nap.");
                                        Thread.sleep(10000);
                                } catch (InterruptedException e) {
                                        e.printStackTrace();
                                }       
                        }
                }
                this.getRace().reportFinished(this);
        }
}

=== ... and new rules ===
How do we make the Hare a instance of the FastAndLazyContender ? The answer is we use a factory that creates a Object depending on the type attribute of the contender element.

File: ContenderFactory.java

package com.ullgren.pontus.test.digestrace;
 
import org.apache.commons.digester.AbstractObjectCreationFactory;
import org.xml.sax.Attributes;
 
public class ContenderFactory extends AbstractObjectCreationFactory {
 
        @Override
        public Object createObject(Attributes attributes) throws Exception {
                String type = attributes.getValue("type");
                if ( type.equals("FastAndLazy")) {
                        return new FastAndLazyContender();
                }
                else {
                        return new Contender();
                }
        }
 
}

We also create a new rule file that uses this factory to create the contender objects.
File: second-race-rules.xml

<?xml version="1.0"?>
 
<digester-rules>
   <pattern value="race">
      <object-create-rule classname="com.ullgren.pontus.test.digestrace.DigestRace" />
           <call-method-rule pattern="distance" methodname="setDistance" paramcount="0" />
   </pattern>
 
   <pattern value="race/contender">
      <factory-create-rule classname="com.ullgren.pontus.test.digestrace.ContenderFactory" />
      <set-properties-rule>
         <alias attr-name="name" prop-name="name" />
      </set-properties-rule>   
      <call-method-rule pattern="velocity" methodname="setVelocity"
                        paramcount="0" />
      <call-method-rule pattern="title" methodname="setTitle" 
                        paramcount="0" />
      <set-next-rule methodname="addContender" />
   </pattern>
</digester-rules>

=== The moral of the story ===
Now the results look better.

Lets start the second race! The distance is 44444
Mr Hare takes a nap.
Mr Hare takes a nap.
Mr Tortoise finished. Position: 1
Mr Hare takes a nap.
Mr Hare takes a nap.
Mr Hare takes a nap.
Mr Hare finished. Position: 2
Race Finished

As shown ''Slow and steady wins the race.''