Brian Sam-Bodden's complete blog can be found at: http://www.integrallis.com/blog/

Items:   6 to 10 of 17   « Previous  | Next »

Friday, April 30, 2010

One of the factors that made Java hugely successful is the myriad of open source libraries and frameworks. The successful ones have had now a decade or more to mature and grow. A side effect of being successful is both intended and unintended complexity. In the next few pages I will show you how a JRuby Domain Specific Language (DSL) can breed new life into an old powerhouse Java library

In recent years it has become clear that the lasting legacy of Java is not Java “the language” but Java the platform and specially the open-source community that it has fostered over the last 15 years.

Many developers (including yours truly) experienced a great deal of success with the Java language but as the years went by it started to become apparent that the safety “features” of the language that made it so popular were getting in the way of progress. This became painfully clear in the area of Web Development and it became borderline embarrassing when we were introduced to the Ruby on Rails framework and witness the productivity boost that the combination of a dynamic language like Ruby and a DSL-based approach to a framework brought to the table.

Today, we recognize that the Java language is just another player in the world of the Java Virtual Machine. The JVM is an amazing and pervasive piece of software that in the early days of Java played second fiddle to the language. This is no more, everyone know acknowledges that this amazing, highly tuned, multiplatform computing engine dominates the present and permeates the future of computing.

As I write this article there are more than 200 language implementations available for the JVM, many of them now being used in the “enterprise” and not only in academia. I strongly believe that this decade marks the official beginning of the “polyglot” era of programming with the JVM smack in the center of this new world.

Ruby

Ruby is a dynamically typed language created by Yukihiro “Matz” Matsumoto. Ruby is a general purpose, multi-paradigm language inspired primarily by Smalltalk, Perl, Lisp and Eiffel.
As David Heinemeier Hansson puts it “Beauty leads to happiness, happiness leads to productivity, thus beauty leads to productivy”. If you have never worked with Ruby, after a few hours coding it will immediately hit you. Things just make sense! My first thought was “there is no way in hell this was build by committee!” Indeed it wasn’t. Matz set out to create a language for him to enjoy in which the main ideals were that programming should be fun and that the language should behave in predictable ways for those experienced with it core principles (this is often referred as the “Principle of Least Surprise”.

Rather than praise Ruby, let me show some quick examples the simplicity of Ruby. Say we have to do some basic, brain-dead file manipulation; load a text file and print its contents line by line. In Java we would do something along of the lines of:

package com.integrallis.braindeadio;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class MyFileReader {
    public static void main(String[] args) {
        try {
            BufferedReader in = new BufferedReader(new FileReader("read_me.txt"));
            String str;
            while ((str = in.readLine()) != null) {
                System.out.println(str);
            }
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Listing BSB-1

After years of doing Java we have come to accept code like that shown in Listing BSB-1. I equate this code to wearing a deep-ocean, mesh-reinforced diving suit to go to the beach. If you go to the beach there is the possibility that you might go under, and that in the depths of the ocean a great white might come after you. Yes, it is possible but also highly improbable.

With Ruby we don’t even have to commit to create a permanent record of the operation performed in Listing BSB-1, we can get our work done directly in the Interactive Ruby Shell (IRB).

>> File.open('read_me.txt', 'r').each do |line| 
?>   puts line
>> end
Flexibility
Power
Simplicity
Beauty
=> #<file:read_me.txt>
>>

Listing BSB-2

As seen in Listing BSB-2, Ruby provides a File class, which has an open method that takes the name of the file and a file mode (in this case ‘r’ for read-only). So far so good, next is where the Ruby Way kicks in! The result of the call to open returns a File which mixes-in the Enumerable module, which gives it an iterator via the each method. The each method expects a block, the code wrap in the “do” and “end”. We can think of a block as an anonymous function and one of their basic usages of a block is loop abstraction. As opposed to Java, with Ruby we don’t need to peer into the internals of what we are iterating over, instead we provide a function with a known interface to the owner of the collection we want to iterate over. The result is less code and better encapsulation!

But Ruby was designed by a programmer and is therefore for programmers. Methods that would make sense to be included in the core of the language usually are! Therefore it is no surprise that the File class also has a “readlines” method, which returns an array and what do you know! Ruby knows how to print an Array:

>> puts File.readlines('read_me.txt')
Flexibility
Power
Simplicity
Beauty
=> nil
>>

Listing BSB-3

As a fairly accomplish Java developer I can tell you without reservation that Ruby is the most productive environment I’ve ever develop applications in.

JRuby

JRuby is pure Java implementation of Ruby that started in the early 2000s. JRuby aims to bring the beauty and simplicity of Ruby to the Java world and bring the Java ecosystem to the Ruby world. This means that you gain the best of both worlds; From a Java application you get to use Ruby applications and libraries and from a Ruby application you get access to Java Libraries (in a Ruby-eske way).

JRuby also runs significantly faster than most other Ruby implementations (if you don't count VM startup), it supports native threads, Unicode and can be fully compiled ahead of time or just in time.

JRuby also bridges the gap left by Java the language. Dave Thomas once described Java as “the safety scissor of the programming world”. Following that analogy, Ruby is then a samurai sword and via JRuby it brings powerful features to the Java world: Blocks, Closures, Open Classes, Meta-programming and Duck-typing all crucial ingredients when building a Domain Specific Language.

Calling Java From JRuby

In order to enable Java features in a Ruby program we need to add the ‘java’ module to our program. We can also include specific Java packages and group them under a Ruby module (modules are like un-instatiable, namespaced code containers; they can contain classes, methods, constants and free-flowing code)

require 'java'

module Java3D
  include_package "javax.media.j3d"
  include_package "com.sun.j3d.utils.geometry"
  include_package "com.sun.j3d.utils.universe"
end

module Swing
  include_package "java.awt"
  include_package "javax.swing"
end

module Math
  include_package "java.lang.Math"
end

Listing BSB-4

Requiring the Java module gives your Ruby code access to the bundled Java libraries. We used three modules to include and namespace specific Java packages: Java3D; containing multiple Java3D packages, Swing; containing the root AWT and Swing packages and Math; exposing the java.lang.Math class.

We these powerful Java libraries in place we can now let Ruby loose and write some code to use the Java 3D API. The Ruby class ThreeDimensionalCube creates a rotating, multi-colored cube; the power of Java meets the ease of development of Ruby:

class ThreeDimensionalCube
  include Java3D
  include Swing
  include Math

  def self.create_canvas
    config = Java3D::SimpleUniverse.get_preferred_configuration
    canvas = Java3D::Canvas3D.new(config)

    scene = create_scene_graph
    scene.compile

    # SimpleUniverse is a Convenience Utility class
    universe = Java3D::SimpleUniverse.new(canvas)

    # This will move the ViewPlatform back a bit so the
    # objects in the scene can be viewed.
    universe.get_viewing_platform.set_nominal_viewing_transform
    universe.add_branch_graph(scene)

    canvas
  end

  def self.create_scene_graph
    # Create the root of the branch graph
    obj_root = Java3D::BranchGroup.new

    # rotate object has composited transformation matrix
    rotate = Java3D::Transform3D.new
    temp_rotate = Java3D::Transform3D.new

    rotate.rot_x(PI / 4.0)
    temp_rotate.rot_y(PI / 5.0)
    rotate.mul(temp_rotate)

    obj_rotate = Java3D::TransformGroup.new(rotate)

    # Create the transform group node and initialize it to the
    # identity.  Enable the TRANSFORM_WRITE capability so that
    # our behavior code can modify it at runtime.  Add it to the
    # root of the subgraph.
    obj_spin = Java3D::TransformGroup.new
    obj_spin.set_capability(Java3D::TransformGroup::ALLOW_TRANSFORM_WRITE)

    obj_root.add_child(obj_rotate)
    obj_rotate.add_child(obj_spin)

    # Create a simple shape leaf node, add it to the scene graph.
    # ColorCube is a Convenience Utility class
    obj_spin.add_child(Java3D::ColorCube.new(0.4))

    # Create a new Behavior object that will perform the desired
    # operation on the specified transform object and add it into
    # the scene graph.
    y_axis = Java3D::Transform3D.new
    rotation_alpha = Java3D::Alpha.new(-1, 4000)

    rotator =
      Java3D::RotationInterpolator.new(rotation_alpha, obj_spin, y_axis,
      0.0, PI * 2.0)

    # a bounding sphere specifies a region a behavior is active
    # create a sphere centered at the origin with radius of 1
    bounds = Java3D::BoundingSphere.new
    rotator.set_scheduling_bounds(bounds)
    obj_spin.add_child(rotator)

    obj_root
  end

  def show
    frame = Swing::JFrame.new("3D Cube")
    frame.layout = Swing::BorderLayout.new
    frame.add("Center", ThreeDimensionalCube.create_canvas)
    frame.default_close_operation = Swing::JFrame::EXIT_ON_CLOSE
    frame.set_size(256, 256)
    frame.visible = true
  end

end

cube = ThreeDimensionalCube.new
cube.show

Listing BSB-5

We run the example (note that there is no need for a main method) with the last two lines in Listing BSB-5. First we instantiate a ThreeDimensionalCube object and then call the show method on said object. The result is shown in Figure BSB-1.

Bsb Jruby 01

Our Problem Domain: Dealing with XML

One of the pain areas of Java development has always been dealing with XML. We could attribute this to the overuse (or is it abuse) of XML in the Java world. But if you were unlucky enough to deal with the SAX and DOM API in Java you probably don’t have any hair left by now.

I was in the same boat, until I discovered XOM (XML Object Model) is one of the most versatile Java XML libraries in existence. XOM is a Tree-based API like DOM and JDom and it aims for correctness, simplicity and performance. In the Java world XOM provided the best of both worlds, fast parsing and a tree-model that allows navigation of the XML documents.

Using XOM is easy to create well-formed XML documents, for example Listing BSB-6 shows a “Hello World” example available with the XOM distribution.

import nu.xom.Document;
import nu.xom.Element;

/**
 *  * Hello World!
 */
public class HelloWorld {

  public static void main(String[] args) {
    Element root = new Element("root");    
    root.appendChild("Hello World!");
    Document doc = new Document(root);
    String result = doc.toXML();
    System.out.println(result);
  }
}  

Listing BSB-6

In listing BSB-7 shows another (more complex) example available from the XOM distribution. This example prints the headlines from an RSS feed.

import java.io.IOException;
import nu.xom.Builder;
import nu.xom.Element;
import nu.xom.Nodes;
import nu.xom.ParsingException;

public class RSSHeadlines extends MinimalNodeFactory {
    private boolean inTitle = false;
    private Nodes empty = new Nodes();

    public Element startMakingElement(String name, String namespace) {              
        if ("title".equals(name) ) {
            inTitle = true; 
        }
        return new Element(name, namespace);             
    }

    public Nodes makeText(String data) {        
        if (inTitle) System.out.print(data);
        return empty;      
    }

    public Nodes finishMakingElement(Element element) {
        if ("title".equals(element.getQualifiedName()) ) {
            System.out.println();
            inTitle = false;
        }
        return new Nodes(element);
    }

    public static void main(String[] args) {
        String url = 
               "http://bbc.co.uk/syndication/feeds/news/ukfs_news/world/rss091.xml";
        if (args.length > 0) {
          url = args[0];
        }
        
        try {
          Builder parser = new Builder(new RSSHeadlines());
          parser.build(url);
        }
        catch (ParsingException ex) {
          System.out.println(url + " is not well-formed.");
          System.out.println(ex.getMessage());
        }
        catch (IOException ex) { 
          System.out.println(
           "Due to an IOException, the parser could not read " + url
          ); 
        }
    }
}  

Listing BSB-7

If you ever built or parsed XML with any other Java API (other than JDOM or dom4J) you would be able to appreciate what XOM can do for you.

Close but not Cigar!

Although XOM does a great deal of heavy lifting for you, if you are Rubyist, Listings BSB-6 and BSB-7 feel like they reveal too much about how XOM works internally and hide the intent of the code with too much plumbing.

Ruby has an XML building library, created by Jim Weirich called XML Builder (or Builder for short). Builder is a Ruby DSL for constructing XML documents. For example, to recreate the “Hello World” example we could do write a simple Ruby program like that shown in Listing BSB-8

require 'rubygems'
require 'builder'

builder = Builder::XmlMarkup.new
builder.root "Hello World!"

puts builder.target!

Listing BSB-8

So what makes Builder a DSL? Note the call to the method “root”. As a Java developer your first reaction is “root must be a method of this builder object, right?” Wrong! Under the covers the call to the method root is being trap by a hook method called method_missing. Think of it as an event listener that is invoke in the event that an object is sent a message it cannot handle.

Internally, Builder is seeing that we are trying to invoke a non-existent method called “root” and instead adds an XML tag to the underlying document being created.

Builder is not as robust or correct when it comes to building XML when compared to XOM and it is nowhere near as fast. Builder, as clearly stated by its name, only “builds”.

Being the greedy developer that I am, I wanted the best of both worlds. How can I get Builder’s semantics for XML building mixed with the XOM’s ability to efficiently parse and navigate XML with XPath?

Excemel: A JRuby DSL for XML Manipulation

The answer to the previous question is a simple one: Use XOM but make it look like Builder. Enter Excemel! A simple JRuby DSL for XML manipulation. Excemel borrows Builder’s semantics for building an XML document backed by an underlying XOM model. Excemel also allows you to parse and manipulate an existing document and exposes XPath functionality in a simple way.

Let’s start with the simple Hello World example again, as shown in Listing BSB-9.

require 'excemel'

doc = Excemel::Document.new :root => "root"
doc.text! "Hello World!"

puts doc.to_pretty_xml

Listing BSB-9

Excemel uses XOM under the cover to build an XML document. It uses Ruby’s method_missing in the same fashion that Builder does as shown in Listing BSB-10.

require 'excemel'
 
xm = Excemel::Document.new :root => "html" # 
xm.head {                                  #   
  xm.title "History"                       #     
}                                          #                                
xm.body {                                  #   
  xm.comment! "HI"                         #     
  xm.h1 "Header"                           #     <h1>Header</h1>
  xm.p "paragraph"                         #     <p>paragraph</p>
}                                          #   

puts "<---- Plain XML ----->"
puts xm.to_pretty_xml

puts "\n<---- Use XPath to find the title ----->"
puts %[the title is #{xm.query("//title")}]

Listing BSB-10

But that’s where the similarities end. Excemel can do some things that Builder can’t, like reading XML directly from a file as shown in Listing BSB-11.

require 'excemel'

doc = Excemel::Document.new :file => "cd_catalog.xml"
puts doc.to_pretty_xml

Listing BSB-11

Listing BSB-12 shows the full power of a JRuby DSL. Remember the RSS headline extractor example shipped with XOM? With Excemel we can bring intent back to the forefront of our code. In about 1/3 of code of the original XOM version Excemel can more clearly perform the same task (with the same efficiency and accuracy, it is XOM under the covers!)

require 'excemel'

doc = Excemel::Document.new :url =>   
      "http://news.bbc.co.uk/rss/newsonline_uk_edition/world/rss.xml"
headlines = doc.query '//title'
headlines.each {|h| puts h }

Listing BSB-12

Conclusions

I hope that this short walk through the world of JRuby will open your eyes to the power of mixing a powerful dynamic language with a robust, fast and battle-tested Java library. Dynamic languages on the VM are paving the way of the future. Groovy and Ruby via JRuby can help us improve the current state of Java and make programming fun again!

References

About The Author

Brian Sam-Bodden is a developer, author and speaker that has spent over fifteen years building software in variety of languages. In the last four years his concentration has been on the Ruby programming language and the Ruby on Rails framework (although he still gets paid to do Java when no one is watching). Brian heads Integrallis , a Web Consultancy with offices in Columbus, Ohio and Scottsdale, Arizona.

Recommend Brian on Working With Rails


Monday, September 7, 2009

Groovy and Grails has stormed the Java world as the new way to create reliable Web 2.0 applications in a concise and fast manner. The ability to go from the start of a project to the end, especially for a smaller demo, is exponentially faster. This is due to the many features of Grails. Out of the box applications are easier to build, create, test, and deploy because of the convention over configuration methodology. In addition Grails is written in Groovy, a dynamic language that can run on the JVM and interact as if they were Java classes. Groovy gives the power to write dynamic code with power and features that are normally unavailable in Java. Possibly the best example of this power in Groovy and Grails is the creation of the Grails Object Relational Mapper (GORM).

However, we can’t all just go to our managers and say “Hey, let’s rewrite this entire application in Grails”. We would more than likely get laughed out the door. At many companies it also isn’t feasible to ask for an entire framework shift. However, most of us can request “small changes”, especially if they help save time.

With Spring 2.0 and above we can create Spring beans out of Groovy objects. This gives us a nice ability to use Groovy nomenclature in our Spring code (e.g. the Meta Object Programming (MOP), cleaner syntax, etc). However, what we do not get with straight Groovy is a better way to interact with the database. For that we need the power of GORM. With Grails 1.1 that has become significantly easier.

Why Do I Want To Use GORM?

You may be familiar with Hibernate and wonder why you should bother with GORM. Hibernate is a GREAT ORM, and the framework has been developed steadily since its inception. While Spring’s wrapping of Hibernate has made it easier to interact with the database, it is still missing the readability and ease of use that most modern developers want. The following example demonstrates how GORM brings simplicity to Hibernate code. (Please note that although GORM derives from Hibernate any mention of Hibernate here will specifically be in reference to the pure Java Hibernate form.)

This example starts by performing a simple query to retrieve items from the Todo table in the database where name and description matched some given text. When using Spring’s Hibernate Template, the code would look something like this:

public List find(final String name, final String desc) {
final String hqlQuery =        	
    "select t from Todo t where t.name = :name and t.description = :desc";
    return (List) getHibernateTemplate().execute(new HibernateCallback() {
    public Object doInHibernate(Session session) throws HibernateException {
        Query query = session.createQuery(hqlQuery);      
        query.setString("name", name);
        query.setString("desc", desc);
        return query.list();
    }
});    
}

Listing NUS-1


While this code does the job, something more readable would not only make it easier for everyone to understand, but would also cut down on time and errors.

Now if we were using Java Persistence API (JPA) supported queries we could make the code in Listing NUS-1 a bit simpler as shown in Listing NUS-2.

@PersistenceContext
EntityManager manager;

public List find(final String name, final String desc) {
    final String hqlQuery =        	
        "select t from Todo t \
        where t.name = :name and t.decscription = :desc";
    return manager.createQuery(hqlQuery)
        .setParameter(“name”, name)
        .setParameter(“desc”, desc)
        .returnResultList();
}

Listing NUS-2


This is more concise and fairly readable, but several queries in a row would require repeating the same block of code many times with slight alterations and would increase our time and effort to do so. You may be asking yourself what is the problem with this? After all I am sure many of you have witten similar or even more verbose code before. The problem lies in the fact that code should be easily readable. As good developers we want to concern ourselves with the business logic, using the write tools. We shouldn’t have to spend too much of our time deciphering easy parts of the code, and queries to the database are often just that. Using GORM helps us to solve that problem, and here’s how.

Let’s take what that code is doing and write it in English. The code is wanting to query the Todo table to find all by name and description. Well how do we write that out? How about this:

Todo.findAllByNameAndDesc(name, desc)

That is simple, concise, and in GORM that line is actual code. This begins to show the ease of using GORM over Hibernate and we will expand on that concept when using Criteria queries later. But first, let’s see how to use GORM in our Spring application.

Injecting GORM

The interesting thing about writing an article on Using GORM with Spring is that there is barely enough setup required to fill a blog entry, let alone an entire article. In fact, you could almost Twitter the entire extra XML needed; however, for the purpose of learning we are going to go into a bit more detail. We will show not only how to set up GORM in Spring but also various options when using Spring. This will require configuring the Spring beans, installing the right JARs, and, most importantly, understanding the various ways to use GORM in your application.

Configuring the Spring Beans

We are going to go over in this section the necessary modifications to the spring files needed for using GORM with Spring. The modifications here will be to your bean configurations and are independent of whether you are using Spring in web environment or a standalone application. That being said of course if you were to be using Spring in a web environment no modifications are needed for the web specific files (like web.xml) The first thing to do is to add an XML namespace to your bean configuration. This one is specifically for GORM: xmlns:gorm="http://grails.org/schema/gorm"

Once the namespace is set you will be able to use the GORM Session Factory instead of the Hibernate Session Factory. Listing NUS-3 is an example of the GORM bean definition that would go into your bean definition file.

 <gorm:sessionFactory 
     base-package="com.integrallis.demo.domain"
     data-source-ref="dataSource"
     message-source-ref="messageSource">
     <property name="hibernateProperties">
        <util:map>
          <entry key="hibernate.hbm2ddl.auto" value="update"/>
      </util:map>
     </property> 
 </gorm:sessionFactory>

Listing NUS-3


Luckily, this listing isn't too complicated. Often times with Spring beans one has to define the classes being used; however, with the use of XML namespaces that is not necessary Spring is aware based on the namespace used With that being said Listing NUS-3 serves the same purpose as your standard Hibernate definitions usually do in Spring and that is to define the Session Factory. In fact, most of the items defined will be similar to Hibernate Session Factory listings.

Line 1 is the name space reference to the GORM Session Factory, which is fairly standard stuff. The rest really is general Hibernate type configurations that are specific to GORM. Line 2 is used to specify where your domain objects exist. Remember, with Hibernate you have to define your domain objects. There are basically two ways to do this: one is to define a directory full of hibernate XML mapping files, the other is to specify a directory of domain objects. Line 3, data-source-ref, references a data source that you have defined in Spring. This can be a Java Naming and Directory Interface (JNDI) data source, in memory data source or a more database specific data source. Line 4, message-source-ref, is specific to GORM itself. GORM allows one to define validation constraints in its domain, these constraints then print out messages via a message resource bundle. In Grails this message resource bundle is a predefined name and area, with GORM you will have to specify that item yourself. In GORM these validators are in a specific place with a specific name, but with Spring one can customize the location and name of this resource bundle. On line 5 we start to define the Hibernate properties. If you were using Grails these items would be specified inside your DataSource.groovy file and if you were using JPA would be in the persistence.xml file.

JAR Files Needed

In addition to defining the GORM Session Factory we will also need a few of the Grails JAR files to make this work. Only three jars from Grails are needed and can be included either manually or via your Ivy/Maven configurations:

  • grails-bootstrap 1.1
  • grails-gorm 1.1
  • grails-web 1.1

Configuring the Domain Class

When configuring domain classes with Hibernate the classes are JavaBeans. When using GORM they have that JavaBean look but with less baggage (i.e. no getters / setters needed with Groovy). In addition, we are able to set validations, mappings, constraints, etc with our GORM domain objects. All of that will still be available when using GORM with Spring. However, one important addition needs to be made in order to make GORM work with Spring. The @Entity annotation needs to be added to the class level domain object so that it is recognized as a GORM object by the container. Listing NUS-4 is a partial listing of a Todo domain class with the @Entity annotation.

package com.integrallis.demo.domain
import grails.persistence.Entity

@Entity
class Todo {
	String name
String descr
	// Any GORM constraints and mappings
}

Listing NUS-4


Using GORM to Create Criteria Queries

One of the biggest headaches I have ever had with Hibernate was using Criteria queries. These are extremely powerful queries whose biggest advantage is that you are able to create dynamic queries without having to do string concatenation of SQL / HQL strings. Essentially the developer will tell the Criteria object what field(s) to do comparisons against and then to execute the query against a specific domain. Here’s an example of a simple criteria in Hibernate for querying all the records where name or description equal some parameter.

Session.createCriteria(Todo.class)
    .add(Restrictions.or (
Restrictions.eq("name", someParam),
Restrictions.eq("description", someParam)
) ).list()

Listing NUS-5


Part of the downside to this code is the readability. Never mind the fact you also had to remember to inject the session. The fluidity of this is not as nice as it could be; however, that is corrected using GORM. With GORM criteria queries have a Domain-Specific Language (DSL) associated with them, making the readability and usability easier. Not to mention there is no need to tell the container to inject sessions, data sources, etc. In a purer way all you are dealing with is the domain you are using. Listing NUS-6 is the query from Listing NUS-5, but written in GORM.

Todo.createCriteria().list() {
    or {
        eq("name", someParam)
        eq("description", someParam)
    }
}

Listing NUS-6


This code performs the same query as the Hibernate code did above albeit a bit cleaner. Part of the idea with using GORM is that your code becomes more readable. For one thing, the fact that it is an "or" restriction is much more prominent. However, the skeptic in mind can easily look at the first query and realize using static imports would shorten the text itself. Static imports allow you to define the static methods on classes as imports at the top of your code. So with static imports one would be able to shorten Restrictions.or to or. And while that is true, there is still another value added feature with the GORM that is missing from the Java query: the ability to actually embed code in the DSL. If we want to use a for loop or some simple checks before adding the restrictions, we will break up the readability of the Hibernate Criteria and have to start to build each section of the normal Hibernate query in chunks. However, that is not the case with GORM Criteria queries.

We can insert the code directly inside the closure, so suppose we actually want to pass the name of the field and the value in a map and "or" it against all passed in fields? With GORM that’s simple and DOESN’T breakup the readability.

Todo.createCriteria().list() {
    or {
        map.each() { key, value ->
            eq(key, value)
        }
    }
}

Listing NUS-7


Since Listing NUS-7 is a Groovy closure, one can add any code or call any method from within the closure itself. This provides elegance and simplicity not normally found in Java criteria queries.

Using Groovy or Java?

Hopefully in the preceding section I have convinced you to consider using GORM inside of Spring. Now lets take a look at the practical implications of integrating GORM, which uses meta programming features, with Java and Groovy code.

First off, if your existing code or the code you want to use GORM with is Groovy you are 100% fine. You can use everything we discussed with ease. However, if you are going to start using GORM in existing straight Java code the integration is not AS seamless but it is still pretty easy. Groovy, in the end, compiles down to Java byte code, which allows your Java code to fully interact with the Groovy code. However, this does come with a few caveats and the biggest one does affect your ability to use GORM. One is the way in which Groovy works. Groovy can work just like any regular Java class with methods and properties. By default when you create a method in Groovy that looks like

def myMethod {
  "joseph"
}

When this is compiled and consumed by a Java class the method will assume that it is returning a java.lang.Object. If one had defined an actual return it would assume the actual return. These items work extremely transparently. However in Groovy one can intercept method calls to the class for methods that do not exist already on the class. One way to do this is by defining a method called methodMissing on the Groovy class. This method will then have passed into it the name of the method that was called and all the parameters passed into it as well. It is then the responsibility of this method to determine based on the name what to do.

In theory one could easily make use of the same features with Java; however, since Java has to get compiled first you would throw errors because the compiler could not see the method at compile time. With Groovy, and other dynamic languages, we do not have this limitation. And it is this power that makes the dynamic queries like Todo.findByName() work. However, this will also have consequences when interacting between Java and Groovy that we will discuss below. So what are your choices?

  1. Convert the classes you have that are Java based that you want to use GORM with into Groovy classes. Now this may seem like quite the cop out of an answer but truthfully, why not? Aside from removing the public from the class declaration and changing the extension of the file name, the code can remain the same. Not only that but now your class will have access to all of Groovy and Java’s abilities.

  2. If the first way scares you then another method is creating the traditional Data Access Object (DAO) class. In this case your DAO would be a Groovy file that would mainly serve to call the domain class. For the most part, these classes will probably only be about one line long for many queries, but may be a bit longer if you are using HQL or Criteria queries. One advantage of this methodology is that it is easier to perform unit testing on not only the service calling it, but the DAO itself.

  3. The last solution takes the idea of using a DAO but does it in more of a Grails way. That is to put your database interactions directly on the domain class in the form of concrete static methods. This methodology can also be used if you pick the first method and are using non MOP created queries.


Performance Considerations

One of the odd things with Java developers and their reasons against using Groovy is the "performance considerations". The reason I say this is odd is because there was a similar argument in the late '90s between the C world and the Java world. The argument basically boiled down to which was faster and does it matter. Well that argument has arisen again when comparing Groovy to Java, so I wanted to take a look and see if it matters and by how much. And while the Groovy code may be slower, is it really going to matter when factoring in the time delays when interacting with the database.

Before we start let’s remember to take these numbers as an example and not the Holy Grail of performance calculations. This is not just running two classes but running two classes and a whole lot of overhead that can easily vary. Even within our runs you will see subsequent runs of the same code will vary. We have the JVM to contend with, when Garbage collection is running, the MySql database all factors in contributing to time and that can cause differences.

So my experiment was fairly simple: I wanted to test inserting and retrieving from the database. The first test created a for loop that creates a thousand records and saves them. The second test, which performs a query based off of description, returns a thousand records as well (because all my descriptions I inserted were the same). To make things a bit tougher on GORM I used a MOP query instead of an HQL query. The Java version uses a Hibernate HQL query. I had considered using a straight JDBC query for the Java portion of the tests, but I felt that would not necessarily be a fair comparison since most of us would not use straight JDBC. In addition these tests were both ran on a MySql database.

The Results

We will take a look at the results below which may surprise you. For the experiment I ran each set three times through the UI using Springs PerformanceMonitorInterceptor to capture the timings of the DAO call.

Record Insertion

The first batch was to test inserting 1000 records into a Todo table using GORM versus using Java Hibernate.

Run 1 Run 2 Run 3
GORM 1438 ms 711 ms 625 ms
Hibernate – Java 640 ms 491 ms 412 ms

I found the results interesting mainly due to the time it took GORM for the FIRST time it ran. While the Hibernate inserts ran faster they both steadily progressed at a faster rate for each subsequent test. Both results in the end were fairly good though.

Record Retrieval

The second set of tests checked the performance of retrieving from the database. In addition, Hibernate and GORM would have to create actual Todo objects of each record and return them in a List back to the calling service.

In this case I wanted to show you the queries used since it’s not as straight forward as insertions. For the Groovy query we used the MOP query:

def findByDescription(String s) {
    Todo.findAllByDescription(s)
}

For the Hibernate example we used the HibernateDAOSupport subclass that comes with Spring to create a one line query:

public List findByDescription(String desc) {
    return getHibernateTemplate().find("From Todo t where t.description = ? ", desc);
}

The results actually surprised me a bit.


Run 1 Run 2 Run 3
GORM 85 112 131
Hibernate – Java 199 244 254

The GORM query actually performed FASTER. Without further investigation I cannot tell you why. . These tests are obviously not an "end all, be all" in performance comparisons but at least give you some ideas when weighing in on whether to use GORM.

Conclusion

Interacting with the database in traditional Spring applications can be a tedious process; however, I hope what I’ve shown here is that using GORM to create our Spring applications can make the process much easier. Using a dynamic language has advantages over traditional static languages; readability and the ease to create being the major ones. In the end, using GORM, you will have cleaner and more maintainable code, which should be a goal for all of our projects.

About The Author

Joseph Faisal Nusairat, is the co-author of Beginning Groovy & Grails and author of Beginning JBoss Seam. Joseph is the in-house Groovy/Grails/Java expert at Integrallis , a Web Consultancy with offices in Columbus, Ohio and Scottsdale, Arizona.


Wednesday, August 19, 2009

A Java Rule Engine for the rest of us

For most Java developers the idea of using a Rule Engine evokes thoughts of vendors in suits selling their bosses a complex and expensive piece of software they don’t need and the introduction of something completely foreign and intrusive to their code base. Drools 5 (http://www.jboss.org/drools/) aims to change this perception by bridging the gap between the Java developer and world of Rule-based systems.

The Quick and Dirty on Rule Engines

The simplest explanation of what a rule engine is that is a very efficient pattern matcher. It matches data, referred to as “facts” against rules. Rules are simple if-then constructs that operate on the matched data. For example, imagine an example application in which the data is a loan application containing the credit score for the loan applicant. A rule could express a credit score requirement for a loan such as:

"A loan application for ACME loans for which the loan applicant has a credit score lower than 680 will be rejected"

For the context of the this example assume that the following Java classes exist:

    Mortgage: Represents a loan product belonging to a lender. It contains amongst other data, the name of the lender providing the loan.
  • LoanApplication: Represents an application for a specific loan product. It contains information specific to the applicant such as the credit score and the lender associated with the application.
  • RejectionNotice: Represents a communication to the applicant that the application has been rejected
rule "LowCreditScoreRejection"
dialect "java"
    when
        mortgage:Mortgage(
            lender:lenderName == "ACME"
        )
        application:LoanApplication(
            lender == mortgage.lenderName,
            score:creditScore < 680
        )
    then
        application.reject("the score " + score + " is too low. \n" +
                           "A credit score of at least 680 is required");
        insert(new RejectionNotice(application));
end

Listing SAM-1 A simple rule

The rule named “LowCreditScoreRejection” is a typical Drools Rule Language (DRL) rule. It has two parts; the “when” part, also known as the “predicate”, “premise”, “condition” or simply as the “Left-hand side” (LHS for short) and the “then” part or “consequence”, “action”, “conclusion” or “Right-hand side” (RHS)

The Rule Condition

The “when” part or rule condition determines the patterns to be matched. That is, the types and characteristics of the objects that will activate the rule. In the example shown, we are looking to match two objects, an object of type Mortgage and an object of type LoanApplication. The “mortage” object must have a lenderName (mortgage.getLender) equals to “ACME” and the “application” must have a matching lender name and a creditScore (application.getCreditScore) that is less than 680.

As you can see this rule only gets evaluated if there are two objects of the aforementioned types present and it is only activated if those two objects properties match the conditions in parenthesis.

An observant Java developer will notice that the rule sort of looks like Java code but not quite. The when part list the classes of the objects that must be present and the values of the properties of those instances needed to activate the rule. The when part is purely a pattern matching expression using first order logic. You can think of the when part as the “where” clause in a SQL statement. Just like in a SQL statement you can create aliases for the objects being matches (and their properties). In the “LowCreditScoreRejection” rule we have three aliases “mortgage”, “lender”, “application” and “score”. Once you have aliased a matched object that object can be used somewhere else in the rule condition and also in the rule consequence.

The Rule Consequence

We learned that the rule condition part of the rule determines what pattern of objects that will activate the rule. The “then” part or rule consequence is what happens when the conditions set forth in the “when” part are met. In Drools the consequence is simply a block of Java code. In the “LowCreditScoreRejection” rule example there are two things happening in the “then” part. First we are calling the “reject” method on the application and passing a message telling them why the application is being rejected (notice the use of the alias “score”). Next and last we are creating a new object of type RejectionNotice, passing the application object in the constructor and then passing the newly created object to the insert method. The insert method is a Drools working memory method that tells the rule engine that there is a new object that should be considered when evaluating the rules. In this case the expectation is that there will be another rule that has a condition expecting objects of type RejectionNotice and that will act upon them.

The Rule Engine

From the simple example of the “LowCreditScoreRejection” rule we see that a rule engine is a system that matches facts (our data objects) against rules. The rules are then used to infer conclusions about the data. In the example, the conclusions inferred were rejection of the loan application and the creation of the rejection notice. This type of system is what is referred to as a data-driven forward chaining reasoning system. At the heart of the system is an “inference engine”; the component that does the pattern matching, activates the rules and determines how to execute the activated rules. This process is typically referred to as truth maintenance. Under the covers Drools uses a custom version of the popular Rete algorithm (see http://en.wikipedia.org/wiki/Rete_algorithm). As we can see the “forward chaining” process starts with the available facts and uses the rules to infer more facts (such as RejectionNotice) until a desired goal as been reached (determining whether a loan application is approved or rejected and communicating the rejections).

Main Drools Classes

The rules like “LowCreditScoreRejection” rule are contained in DRL files (files with the extension .drl) that comply with Drools native rule language syntax. To use the created rules in a Java application you must:

  1. KnowledgeBuilder: A knowledge builder is the class that knows how to load and process DRL files. The impact of parsing and building in memory objects from the DRL file happens at this point. An instance of a knowledge builder can be obtained from the KnowledgeBuilderFactory using the newKnowledgeBuilder() method
  2. KnowledgePackage: A knowledge package is the result of processing a DRL file; a serializable object. The knowledge builder has a getKnowledgePackages() that returns a collection of knowledge packages.
  3. KnowledgeBase: A knowledge base is where the knowledge packages are deployed to so that they can be used at runtime. The knowledge base is the object that will be used to create knowledge sessions. This knowledge base is a thread safe construct that serves as a factory of sessions.
  4. KnowledgeSession: The knowledge session is the object used to interact with the rule engine. It is throught a session that you will make the rule engine aware of the facts (the data) that the rule engine will evaluate against the available rules. Inside the Rule Engine

Inside the rule engine, after you have inserted your facts into a knowledge session and invoked the session fireAllRules() method:

  1. Rules are matched against the facts in the knowledge session using the pattern matcher (Rete algorithm)
  2. The unordered list of activations (the Rules that apply based on the facts) is ordered internally (by a conflict resolver) in the session’s agenda
  3. The first Rule in the agenda is the executed, possibly triggering steps 1 and 2 again

This loop is how a Rule Engine infers knowledge from existing facts using rules. You can see that a Rule Engine using pattern matching to reduce/transform the problem space to arrive at a set of facts that can be considered a solution to the problem at hand.

Getting Started with Drools 5: Classifying you Twitter network

Rule Engine development introduces enough conceptual complexity (mainly inherited from the A.I. lingo and academia) that feels fairly unapproachable to us Java developers. So, let tackle a simple but yet representative problem using Drools 5.

Ranking and Classifying Twitter Users

Recently Twitter has become the darling of the social media applications. Classified by some as micro blogging, Twitter’s goal is for users to constantly answer the question “What are you doing?” Of course, the intended usage of a tool by its creators has no bearing on how people will actually use the tool. As an active Twitter user it is especially annoying to deal with those attempting to exploit the tool in ways detrimental to other users experience.

Yes, I am speaking about spammers! Recently blogger Allan Young’s wrote about what he termed the “Twitter Influence Ratio” (http://allantyoung.com). In that blog entry he wrote about a simple way to measure a Twitter user’s influence as the ratio of the number of followers to the number of people the user is following. The article talks about the inexactitude of the measurement for certain notable users such as Robert Scoble.

Based on the twitter influence ratio, Evan Prodromou came up with a simple scale to classify twitter users:

  • 1:5 => Twitter Caster
  • 1:2 => Notable
  • 1:1 => Socially Healthy
  • 2:1 => Newbie
  • 5:1 => Twitter Spammer

Twitter Drools 5.0 Ranking / Classifier Tool

The goal of our Drools 5 project will be to corroborate or repudiate the classification above by also providing a number than can be used to further narrow down the classification. Reading of the possible ways people judge whether a user is a potential spammer (see references) I’ve collected what I think are a small set of rules that can help us narrow down a Twitter user classification:

  • “User has no picture”: A characteristic of spammers (but also of newbies) is that they don’t have a picture set other than the “brownie” icon that Twitter uses by default. This rule will subtract 30 points from the user ranking if he or she doesn’t have a picture set.
  • “Follower with no mutual followers”: If a user follows you but none of your followers also follow he or she. This at best seems to be an indication that the user following is a total different social circle. This rule will subtract 10 points.
  • “Follower no being followed back”: If a user follows you but you are not following them back. This will indicate that the interest is one sided (of course you could also have forgotten to follow back). This rule will subtract 5 points.
  • “Inactive follower”: If a user follows you but has not produced any tweets in the last 30 days, we will consider that user inactive and subtract 15 points.
  • “Low Activity follower”, “Medium activity follower”, “High activity follower” and “Hyper activity follower”: These four rules will check the average tweets per day for a user and classify them based on the following ranges: 0.0-0.5 is Low, 0.5-3.0 is Medium, 3.0-5.0 is High, > 5.0 is Hyper. The activity rules will add/subtract -5, +5, +10, +15 points respectively.

You might be asking how scientific or statistically accurate the rules above are, and the answer is: “I haven’t a clue”. These rules are exploratory, just a learning algorithm the rule author can use rules to discover hidden patterns in the data. What I’m attempting to do here to set a framework that can be easily tweaked and enhanced.

Interacting with the Twitter API

One of the big decisions you’ll face with implementing a rule-based system with Drools is how much Java to put in your rules. As with any other object-oriented application we want encapsulate complex behavior to make our rules more readable. There are also things that are much easily accomplished, tested and developed outside of the realm of the rules engine.

To deal with the interaction with Twitter I decided to use Twitter4J (http://yusuke.homeip.net/twitter4j), a Java library to interact with the Twitter API. Using Twitter4J I created a simple collection of static utility methods contained in the class TwitterUtils.java. Some of the available methods are:

  • Double getTwitterInfluenceRatio(User user)
  • Boolean hasSetProfileImage(User user)
  • Double averageTweetsPerDay(User user)
  • Boolean inactiveForTheLast(User user, Integer days)
  • Integer followersInCommon(Twitter twitter, User target)
  • Boolean isFollowing(Twitter twitter, String target)

Twitter4J provides the classes twitter4j.Twitter which represents the authenticated Twitter user and twitter4j.User which represents detailed information about a Twitter user.

Classifying Users using the Twitter Influence Ratio

Implementing a Rule Based system is all about choices and trade-offs. The first rule we’ll develop is an example of such a trade off. The rule is more of a utility rule to extract and classify the users. To accomplish this I’m using a simple Java enumeration called TwitterUserType that contains a static method to return the right enumeration value given a twitter user’s influence ratio:

public enum TwitterUserType {
    UNCLASSIFIED      (Double.MIN_VALUE, 0.0),
    TWITTER_CASTER    (0.0, 0.2),
    NOTABLE           (0.2, 0.5),
    SOCIALLY_HEALTY   (0.5, 1.0),
    NEWBIE            (1.0, 2.0),
    POTENTIAL_SPAMMER (2.0, Double.MAX_VALUE); 

    private Double low, high;

    TwitterUserType(Double low, Double high) {
        this.low = low;
        this.high = high;
    }

    public static TwitterUserType getType(Double influenceRatio) {
        for (TwitterUserType userType : EnumSet.range(TWITTER_CASTER, POTENTIAL_SPAMMER)) {
            if ((influenceRatio > userType.low) &&
                (influenceRatio <= userType.high)) {
                return userType;
            }
        }

        return UNCLASSIFIED;
    }
}

Listing SAM-2 A Java Enum to classify Twitter Users

In Drools 5 we can create custom data types right at the DRL level using the “declare” keyword:

declare Follower
   user : User
   classification : TwitterUserType
   follows : Twitter
   hasPicture : Boolean
   followedBack : Boolean
   inactive : Boolean
   averageTweetsPerDay : Double
   followersInCommon : Integer
   followeesInCommon : Integer
   ranking : Double
end

Listing SAM-3 A DRL custom data type

In Listing SAM-3 we declare a simple POJO called Follower that will contain some of the metrics used by the rules. The Rule “Extract and classify followers” will match any object of type Twitter (the Twitter4J class representing the authenticated Twitter user), extract its followers and for each of the follower it will create a Follower object and set its values using the static methods in TwitterUtils. Each object created will be then inserted into the knowledge session using the insert method.

rule "Extract and classify followers"
    dialect "java"
    when
        twitter : Twitter()
    then
        for (User user : twitter.getFollowers()) {
            Follower follower = new Follower();
            follower.setUser(user);
            follower.setFollows(twitter);
            follower.setClassification(TwitterUserType.getType(TwitterUtils.getTwitterInfluenceRatio(user)));
            follower.setHasPicture(TwitterUtils.hasSetProfileImage(user));
            follower.setFollowedBack(TwitterUtils.isFollowing(twitter, user));
            follower.setInactive(TwitterUtils.inactiveForTheLast(user, 30));
            follower.setAverageTweetsPerDay(TwitterUtils.averageTweetsPerDay(user));
            follower.setFollowersInCommon(TwitterUtils.followersInCommon(twitter, user));
            follower.setFolloweesInCommon(TwitterUtils.followingInCommon(twitter, user));
            follower.setRanking(0.00);
            logger.info("Inserting follower => " + user.getScreenName());
            insert(follower);
        }
end

Listing SAM-4 A utility rule to extract and classify Twitter followers

One of things that you’ll discover early on is that your application domain objects might not be well suited to be used as rule engine facts. In the case of this simple tool, having an simple data object such as the DRL specific Follower object makes the rule creation simpler and consequently makes the rules much more readable and easy to maintain.

Writing the Twitter Ranking Rules

With the Follower objects created and inserted into the knowledge session we can now write a set of rules that match Follower objects with certain characteristics.

The “User has no picture” rule matches any object of type Follower where the hasPicture boolean value is false. It aliases the matched object as “follower” and in the consequence it subtract 30.0 points from the ranking value.

rule "User has no picture"
    dialect "java"
    when
        follower : Follower(hasPicture == false)
    then
        follower.setRanking(follower.getRanking() - 30.0);
end

Listing SAM-5 The “User has no picture” rule

The “Follower with no mutual followers” rule is equally simple:

rule “Follower with no mutual followers”
    dialect “java”
    when
        follower : Follower(followersInCommon == 0)
    then
        follower.setRanking(follower.getRanking() – 10.0);
end

Listing SAM-6 The “Follower with no mutual followers” rule

As you can see once we created and populated objects suitable for the rule engine, writing the rules becomes a simple task. The rest of the rules are left as an exercise for the reader (or you can download them with the complete sample application on github).

Writing the Java Application

The Java application that will exercise our Twitter rules is a simple class with a main method. We’ll pass a Twitter username and password as arguments via the String[] arguments. The code needed to read the DRL file and create a knowledge package is stardard boilerplate Drools code as shown in Listing SAM-7:

// get a knowledge builder
KnowledgeBuilder knowledgeBuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();

// parse and compile the DRL file
knowledgeBuilder.add(
    ResourceFactory.newClassPathResource("TwitterRules.drl", TwitterDroolsExample.class),
    ResourceType.DRL);

// check the builder for errors

if (knowledgeBuilder.hasErrors()) {
    logger.error(knowledgeBuilder.getErrors().toString());
    throw new RuntimeException("Unable to compile \"TwitterRules.drl\".");
}

// get the compiled packages (which are serializable)
Collection pkgs = knowledgeBuilder.getKnowledgePackages();

// add the packages to a knowledgebase (deploy the knowledge packages).
KnowledgeBase knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase();
knowledgeBase.addKnowledgePackages(pkgs);

Listing SAM-7 Loading and compiling the rules

With a knowledge base in place we can now create and configure a knowledge session as shown in Listing SAM-8:

StatefulKnowledgeSession knowledgeSession = knowledgeBase.newStatefulKnowledgeSession();
Twitter twitter = new Twitter(twitterUser, twitterPassword);
knowledgeSession.insert(twitter);
knowledgeSession.fireAllRules();

Listing SAM-8 Asserting facts and firing the rules

The knowledge session is created using the knowledge base and then we insert a Twitter4J Twitter object which will be matched by our “Extract and classify followers” which in turn will produce Follower objects that will be evaluated by the ranking rules.

Extracting the results

The astute reader will notice that the DRL-scoped Follower objects represent the “result” of our process. The question is then, how do we retrieve those objects from the knowledge session after the rules have executed. Although we could put System.out.println statements in our rules consequence blocks or even logging statements in a real application you’ll most likely need to retrieve those objects from the knowledge session to be used in your Java application.

To retrieve an object from the knowledge session Drools provides a querying facility. Drools queries are just like rules that have no consequence block. For example if we wanted to just retrieve all Follower objects in the knowledge session we could write a query like:

query "get all followers"
    follower : Follower()
end

Listing SAM-9 A Drools Query

The Drools query in Listing SAM-9 simply matches any objects of type Follower and aliases those as “follower”. In the Java code then we could use the query as shown in Listing SAM-10 to retrieve the results:

FactType followerType = knowledgebase.getFactType( "org.drools.examples", "Follower" );

QueryResults results = knowledgeSession.getQueryResults("get all followers");

for (Iterator i = results.iterator(); i.hasNext();) {
    QueryResultsRow row = i.next();
    Object follower = row.get("follower");
    User user = (User) followerType.get( follower, "user" );

    TwitterUserType type = (TwitterUserType) followerType.get( follower, "classification" );
    Double ranking = (Double) followerType.get( follower, "ranking" );
    logger.info(user.getScreenName() + " is a " + type + " with a ranking of " + ranking);
}

Listing SAM-10 Using a Drools Query and dealing with custom data types

Running the application

Running the application will produce output similar to:

stuarthalloway is a TWITTER_CASTER with a ranking of -5.0
bmaso is a NEWBIE with a ranking of 5.0
bobmcwhirter is a NOTABLE with a ranking of 5.0
jaredrichardson is a NOTABLE with a ranking of 10.0

As we can see from the output the numeric ranking begins to shed light on the influence and intentions of your followers in Twitter. As we can see above our friend Stu is classified as a Twitter Caster but due to his low activity or average tweets per day, our rules took 5 points of his ranking while Jared is ranked a little lower, as a Twitter Notable but due to his high activity he get a ranking of +5. The next step is to tweak our rules based on observation and investigation of the flagged users. We can see that the simple Twitter Influence Ratio is not sufficient to accurate predict a twitter spammer. But if we continue adding rules that go deeper than the simple static analysis we’ve perform here we can start getting closer to our goal. With a few more rules, possibly taking advantage of semantic analysis we could look at the hash tags, URLs embedded in tweets and other content analysis and more accurately classify Twitter users.

Conclusion

Rule engines can provide a Java developer with an environment in which logic and data are clearly separated. In the simple example used in this article it is easy to see how the DRL file becomes you laboratory of centralized knowledge about the problem at hand. Once the plumbing code is in place you can truly concentrate on the “business logic” in atomic, discrete, manageable chunks.

In this article we’ve barely scratched the surface of the capabilities provided by Drools. Drools 5 is a complete offering that includes the rule engine (Drools Expert), a Business Rule Management System (Drools Guvnor), a process/workflow engine (Drools Flow) and an event processing/temporal reasoning engine (Drools Fusion).

Resources

The code for the example above can be found at https://github.com/bsbodden/drools-twitter

About The Author

Brian Sam-Bodden is a developer, author and speaker that has spent over fifteen years building software in variety of languages. In the last four years his concentration has been on the Ruby programming language and the Ruby on Rails framework (although he still gets paid to do Java when no one is watching). Brian heads Integrallis , a Web Consultancy with offices in Columbus, Ohio and Scottsdale, Arizona.


Monday, January 14, 2008

Rails

In this installment we are going to build the Dashboard page of the Tempo application. The first incarnation of the Dashboard page will serve as the entry point/main page of the application and its main function will be to provide an interface for users to report time against a project-activity combination.

The user story that we are targeting is:

TMPO-29: “User enters Time” Business Critical [OPEN] 40 points 0% unassigned

The description above is from the Agile Project Management and Collaboration tool Savila. The “User enters Time” story is business critical, it is still OPEN, we have estimated 40 complexity points to it, is 0% completed and it has not been assigned to any team member (or no team member has volunteered for it!)

The description of the user story is "As a User I would like to enter time against a project and activity by providing a starting and ending time"

User Interface

Based on the story description I’ve come up with a rough sketch of what I picture the Dashboard page looking like after the first pass:

tempo dashboard sketch

The sketch above shows 3 different areas:

  • Date Selection: A Calendar-style component that will enable the user to select a given (active) date to enter time
  • Time Entry Details: A form that will provide drop-downs for the Project, Activity, Start Time, End Time for the TimeEntry as well as a text area for a description. Optionally the user will be allowed to enter time as a number of hours to be reported against the given date (with no specific start or end times(1)).
  • Daily Summary: A table displaying the time entries reported for the active date.

(1) Notice that I’ve just created a new requirement that was not present in the original User Story. In our work to fulfill the main User Story we will set the stage to satisfy this new requirement but we should really create a new issue (story) to tackle that new piece of functionality (of course first we should check with the project owner and/or stakeholders)

Dashboard Controller

I started by creating the skeleton code and tests for the Dashboard Controller:

/> script/generate rspec_controller dashboard   
      exists  app/controllers/
      exists  app/helpers/
      create  app/views/dashboard
      exists  spec/controllers/
      exists  spec/helpers/
      create  spec/views/dashboard
      create  spec/controllers/dashboard_controller_spec.rb
      create  spec/helpers/dashboard_helper_spec.rb
      create  app/controllers/dashboard_controller.rb
      create  app/helpers/dashboard_helper.rb

Since we want the dashboard to be the default page for our application we can modify the config/routes.rb accordingly:

map.connect '', :controller => "dashboard", :action => "index"

With the skeleton in place I can now do a little planning about what I need to build.

A bit of Design

Here's what I know about the Dashboard page so far:

  1. The Dashboard page can only be shown if there is a user logged in. (done, taken care by the restful authentication plug-in)
  2. When the user first navigates to the Dashboard page, the current date should be shown in the calendar date picker and on the “blog” style display on the time entry form
  3. The Daily Work table should show all TimeEntries for the current user for the selected date
  4. When the user selects a new date form the calendar date picker the "current date" (the date which under the times entered will be reported) should change.

Create the Views

Let’s start by creating an index.rhtml template in the Views/dashboard directory. The index template will render three partials; one for a blog-style date, the TimeEntry form and one for the Daily Work table. The partials will be _current_date.rhtml, _form.rhtml and _time_entries.rhtml respectively.

views/dashboard/index.rhtml

The listing below shows the contents for index.rhtml. I used some of the CSS styles provided by ActiveScaffold to keep the look and feel consistent.

<div class="active-scaffold">
  <div class="create-view view">  
    <% form_tag :action => 'create' do %>
      <h4><div id="current_date_div"><%= render :partial => "current_date" %></div></h4>
      <ol class="form" >
        <%= render :partial => 'form' %>
        <p class="form-footer">
          <%= submit_tag "Create" %>
        </p>
      </ol>
    <% end %>
  </div>
</div>

<br />
<p>
<div id="time_entries_div">
  <%= render :partial => "time_entries" %>
</div>
</p>   

The index.rhtml view renders a form (which body is contained in the _form.rhtml partial), the current date which is render by the partial _current_date.rhtml. Finally at the bottom we have a section that contains the daily work table summary which is rendered by the _time_entries.rhtml partial. Let's take a look at each one of those partials next:

views/dashboard/_current_date.rhtml

The idea here is to render a blog style data block as shown below:

tempo dashboard sketch

To render the blog style date I have a CSS snippet that I use to style a partial containing the current date. The partial _current_date.rhtml is shown below:

<div class="dateblock">
  <div class="dateblock_month">
    <%= "#{Time::RFC2822_MONTH_NAME[@current_date.month-1]}" %>
  </div>
  <div class="dateblock_day">
    <%= "#{@current_date.day}" %>
  </div>
  <div class="dateblock_year">
    <%= "#{@current_date.year}" %>
  </div>
</div>

Notice that I use an instance variable current_date that will be set in the index method of the dashboard controller if it cannot be retrieved from the session:

  def index
    unless session[:current_date] 
      session[:current_date] = Time.today  
    end
    @current_date = session[:current_date]
  end

In the controller the method to render the partial and update the instance and the session variable follows:

  def current_date
    session[:current_date] = Time.parse(params[:current_date])
    @current_date = session[:current_date]
    render :partial => 'current_date'
  end

The CSS code for the dateblock is shown below (I've found it on a CSS site but I can't remember where):

/*-------------------------------------------------
DATE BLOCK
-------------------------------------------------*/
.dateblock {
  text-align: center;
  width: 50px;
  font-family: sans-serif;
  border: solid 1px black;
  padding-top: 5px;
  color: white;
  background-color:black;
  margin-top: 10px;
  margin-bottom: 10px;
  line-height: 1.22em;
  font-family: sans-serif;
}

.dateblock_month {
  font-size: 12px;
}

.dateblock_day {
  font-size: 26px;
  position: relative;
  top: -2px; 
}

.dateblock_year {
  font-size: 12px;
  position: relative;
  top: -2px;
}

views/_time_entries.rhtml

For the Daily Work table showing all TimeEntries for the current user for the selected date we use the _time_entries.rhtml partial. This partial makes use of ActiveScaffold's ability to embed a scaffold in another controller, or view. To accomplish this we use the

render :activescaffold => 'timeentries'
which will call the render_component passing a specific set of conditions (to be appended to the SQL select statement) and a custom label for the scaffold table:


<%= render :active_scaffold => 'time_entries', 
           :conditions => 
             ["user_id = ? AND YEAR(start) = ? AND MONTH(start) = ? AND DAY(start) = ?",
              @current_user.id, @current_date.year, @current_date.month, @current_date.mday], 
           :label => 
             "Time Entries for #{Time::RFC2822_DAY_NAME[@current_date.wday]}, #{Time::RFC2822_MONTH_NAME[@current_date.month-1]} #{@current_date.day} #{@current_date.year}" %>

In the controller we need to modify the index method to also filter the collection of TimeEntry objects retrieved:

  def index
    unless session[:current_date] 
      session[:current_date] = Time.today  
    end
    @current_date = session[:current_date]
    @time_entries = TimeEntry.find(:all, :conditions => ["user_id = ? AND YEAR(start) = ? AND MONTH(start) = ? AND DAY(start) = ?", @current_user.id, @current_date.year, @current_date.month, @current_date.mday])
  end

We also need a time_entries method to render the TimeEntry(s) based on the current_date in the session:

  def time_entries
    session[:current_date] = Time.parse(params[:current_date])
    @current_date = session[:current_date]
    render :partial => 'time_entries'
  end

The daily work table should now display all the TimeEntry(s) for the current/selected date:

tempo dashboard sketch

views/_form.rhtml

The form partial is fairly simple and provides elements to select the project, the activity, time picker for the start and end dates, an optional total hours worked field and a text area to enter comments associated with the work being reported.

The complete source for the partial is shown below:

<%= error_messages_for 'time_entry' %>

<!--[form:time_entry]-->
<li class="form-element">
  <dl>
    <dt><label for="time_entry_project">Project</label></dt>
    <dd><%= collection_select 'time_entry', 'project', current_user.projects, 'id', 'name' %></dd>
    <%= observe_field 'time_entry_project', :update => 'time_entry_activity', :with => "project_id", :url => { :controller => "dashboard", :action => "load_activities" } %>
  </dl>
</li>

<li class="form-element">
  <dl>
    <dt><label for="time_entry_projects_activity">Activity</label></dt>
    <dd><%= select 'time_entry', 'projects_activity', ['--select a project--'] %></dd>
  </dl>
</li>

<li class="form-element">
  <dl>
    <dt><label for="time_entry_start">Start</label></dt>
    <dd><%= time_picker Time.now, {:time_format => '12', :minute_step => 30, :prefix => 'time_entry', :field_name => 'start', :time_separator => ':'} %></dd>
  </dl>
</li>

<li class="form-element">
  <dl>
    <dt><label for="time_entry_end">End</label></dt>
    <dd><%= time_picker Time.now, {:time_format => '12', :minute_step => 30, :prefix => 'time_entry', :field_name => 'end', :time_separator => ':'} %></dd>
  </dl>
</li>

<li class="form-element">
  <dl>
    <dt><label for="time_entry_hours">Hours</label></dt>
    <dd><%= text_field 'time_entry', 'hours'  %></dd>
  </dl>
</li>

<li class="form-element">
  <dl>
    <dt><label for="time_entry_comment">Long Description</label></dt>
    <dd><%= text_area 'time_entry', 'comment', 'rows' => 12, 'cols' => 45 %></dd>
  </dl>
</li>
<!--[eoform:time_entry]-->

A little Ajax

Let's dissect the form contents. First at the very top we have a form element that displays a drop-down for the projects that the current user is part of. Since we have specific activities per project we need to use some Ajax magic to populate the project activities drop-down.

<li class="form-element">
  <dl>
    <dt><label for="time_entry_project">Project</label></dt>
    <dd><%= collection_select 'time_entry', 'project', current_user.projects, 'id', 'name' %></dd>
    <%= observe_field 'time_entry_project', :update => 'time_entry_activity', :with => "project_id", :url => { :controller => "dashboard", :action => "load_activities" } %>
  </dl>
</li>

<li class="form-element">
  <dl>
    <dt><label for="time_entry_projects_activity">Activity</label></dt>
    <dd><%= select 'time_entry', 'projects_activity', ['--select a project--'] %></dd>
  </dl>
</li>

To accomplish this I use the observe_field tag, Rails lets you listen to events on a field such as the value of a field to which you can respond by making an Ajax call to an action handler with the current value of the field being observed sent to the action handler in the post data of the call. In the case above we are updating the drop-down time_entry by invoking the action load_activities in the controller, which is shown below:

  def load_activities
    project = Project.find(params[:project_id])
    @projects_activities = project.projects_activity
  end

Time Picking

To enable time picking on the form fields for start and end times I used Thong Kuah TimePicker plug-in which can be found at http://rubyforge.org/projects/timepicker/. As described in the read me file, this plug-in follow the usage of ActionView::Helpers::DateHelper closely.

Below is a preview of what the time picking drop-downs will look like:

tempo dashboard sketch

To install the plug-in I again use Piston:

/> piston import svn://rubyforge.org/var/svn/timepicker/trunk vendor/plugins/timepicker
Exported r4 from 'svn://rubyforge.org/var/svn/timepicker/trunk vendor/plugins/timepicker' to 'vendor/plugins/timepicker'

With the plug-in installed you can now use time_picker(datetime, options = {}) in your views.

In our case the usage is shown below:

<li class="form-element">
  <dl>
    <dt><label for="time_entry_start">Start</label></dt>
    <dd><%= time_picker Time.now, {:time_format => '12', :minute_step => 30, :prefix => 'time_entry', :field_name => 'start', :time_separator => ':'} %></dd>
  </dl>
</li>

<li class="form-element">
  <dl>
    <dt><label for="time_entry_end">End</label></dt>
    <dd><%= time_picker Time.now, {:time_format => '12', :minute_step => 30, :prefix => 'time_entry', :field_name => 'end', :time_separator => ':'} %></dd>
  </dl>
</li>

Calendar Picker

For the current date selection I'm using a set of plug-ins that work well with ActiveScaffold. Calendar Date Select can be found at http://code.google.com/p/calendardateselect/. There is also a bridge to integrate it into ActiveScaffold at http://wiki.activescaffold.com/wiki/published/CalendarDateSelectBridge:

/> piston import http://calendardateselect.googlecode.com/svn/tags/calendar_date_select vendor/plugins/calendar_date_select
Exported r154 from 'http://calendardateselect.googlecode.com/svn/tags/calendar_date_select' to 'vendor/plugins/calendar_date_select'
/> piston import http://activescaffold.googlecode.com/svn/bridges/active_scaffold_calendar_date_select_bridge vendor/plugins/active_scaffold_calendar_date_select_bridge
Exported r634 from 'http://activescaffold.googlecode.com/svn/bridges/active_scaffold_calendar_date_select_bridge' to 'vendor/plugins/active_scaffold_calendar_date_select_bridge'
/> 

Once installed, I placed the code for the date picker in the main application layout (application.rhtml). The snippet below shows only when the controller is the dashboard controller and the action is the index action:

<!-- ============ --> 
<!-- Right Column -->
<!-- ============ --> 
<div class="Right">
  <div class="col">
    <% if current_page?({'controller' => 'dashboard', 'action' => 'index'}) -%>
      <h1>Select a Date</h1>
      <%= calendar_date_select_tag 
          :current_date, 
          @current_date, 
          {:embedded => "true",
           :onchange => "new Ajax.Updater('current_date_div', '/dashboard/current_date?current_date=' + $H({current_date: this.value}).toQueryString(), 
                        {asynchronous:true, evalScripts:true}); 
                        new Ajax.Updater('time_entries_div', '/dashboard/time_entries?current_date=' + $H({current_date: this.value}).toQueryString(),
                        {asynchronous:true, evalScripts:true})"} 
      %>
    <% else -%>
      <h1>[[Help Title]]</h1>
      <p>[[Help Body]]</p>
    <% end -%>
  </div>
</div> 

Finally we need a create method in the controller to handle the creation of the new time entry when we submit the form:

  def create
    project = Project.find(params[:time_entry][:project])
    projects_activity = ProjectsActivity.find(params[:time_entry][:projects_activity])
    @time_entry = TimeEntry.new
    @time_entry.user = current_user
    @time_entry.project = project
    @time_entry.projects_activity = projects_activity
    start_time = Time.parse(params[:time_entry][:start])
    end_time = Time.parse(params[:time_entry][:end])
    @time_entry.hours = params[:time_entry][:hours]

    unless session[:current_date] 
      session[:current_date] = Time.today
    end
    @current_date = session[:current_date]
    @time_entry.start = Time.utc(@current_date.year, @current_date.month, @current_date.mday, start_time.hour, start_time.min)
    @time_entry.end = Time.utc(@current_date.year, @current_date.month, @current_date.mday, end_time.hour, end_time.min)

    if @time_entry.save
      flash[:notice] = 'TimeEntry was successfully created.'
      redirect_to :action => 'index'
    else
      render :action => 'index'
    end
  end

In the code above we first find the project and the project activity based on the parameters posted with the form, next we create a new TimeEntry object and set the values on the form. The start and end times on the time entry are calculated using the current_date value and the hour and minutes selected on the time pickers.

tempo dashboard

In the next installment we will move beyond the simple capabilities implemented so far and begin thinking about what it takes to polish a product so that it can be ready for mass consumption. Until the next time.


Thursday, December 6, 2007

Rails

This installment was supposed to be about building the Dashboard page of the Tempo application. Instead, I'm addressing some bugs reported by readers.

Fixing Bugs TDD-style

An observant reader pointed out that one of the tests for the TimeEntry class broke when testing using a date range across a month boundary.

The offending code is shown below:

  def split!
    remaining_time_entries = []
    if needs_splitting
      # save the original end time
      original_end = self.end 
      # first change the current TimeEntry to the end_of_day of the start day
      self.end = self.start.change(:hour => 23, :min => 59, :sec => 59)
      (self.start.day+1..original_end.day).each do |day|
        time_entry = self.clone
        time_entry.start = time_entry.start.change(:mday => day).beginning_of_day
        if day == original_end.day
          time_entry.end = original_end
        else
          time_entry.end = time_entry.start.change(:hour => 23, :min => 59, :sec => 59)
        end
        remaining_time_entries << time_entry
      end   
    end
    remaining_time_entries
  end

Even though the code above was created in a TDD fashion and several of the tests created exercised the code, the tests failed to cover all of the possible cases.

The lesson here is that when dealing with date ranges, you should test across months and year boundaries. The code above only works when the two datetimes are in the same month and year which is a critical flaw. The code uses the day method to loop form the start day to the end day of the range. This will obviously break across months and of course across years.

One good thing about the test above was (as some might argue whether this is a good thing or not) is that I used a date range that started on the current date and time. In a continuous integration scenario this broken test would had revealed itself in the hourly build.

I've also made the same mistake (of using the day method) to check if a range of dates needed splitting, here's the original code:

def needs_splitting
  self.start.day < self.end.day
end

First, let's prove that the code is indeed broken. To accomplish this I've written a test that crosses a month boundary:

it "should know how to split a time entry across multiple days over a month boundary" do
  time_entry = create_time_entry
  time_entry.start = Time.local(2007,"nov",30,23,0,0)
  time_entry.end = 5.hours.since(time_entry.start)
  entries = time_entry.split!
  entries.should_not be_empty
  total = 0.0
  entries.inject(0) {|total, e| total += e.total_hours}
  total += time_entry.total_hours
  total.should eql(5.0)
  time_entry.needs_splitting.should_not be_true
end

The above test fails with the existing code. Now we can refactor the code to pass the test.

  def needs_splitting    
    (self.start.year != self.end.year) ||
    (self.start.month != self.end.month) ||
    (self.start.day != self.end.day) 
  end

  def split!
    remaining_time_entries = []
    if needs_splitting
      # save the original end time
      original_end = self.end 
      # first change the current TimeEntry to the end_of_day of the start day
      self.end = end_of_day(self.start)
      days = ((original_end - self.start) / 86400).ceil.to_i  
      (1..days).each do |day|
        time_entry = self.clone
        time_entry.start = time_entry.start.advance(:days => day).beginning_of_day
        time_entry.end = time_entry.end.advance(:days => day)
        if same_day(time_entry.end, original_end)
          time_entry.end = time_entry.end.change(:hour => original_end.hour, :min => original_end.min, :sec => original_end.sec)   
        else
          time_entry.end = end_of_day(time_entry.start)
        end
        remaining_time_entries << time_entry
      end   
    end
    remaining_time_entries
  end

The needs_splitting method now checks the year, month and day. The split! method now finds the total number of whole days (plus one) in the datetime range. It then creates new TimeEntry(s) for each of the whole days (24 hours each) and a partial one of the last day on the range. To advance each one of the days I use the advance method (see Rails extensions to the Time class).


Items:   6 to 10 of 17   « Previous  | Next »