My favorites | Sign in
Project Logo
                
Search
for
Updated Sep 22, 2009 by earllambertfuneralfund
Labels: Featured, Phase-Support
HannibalExamples  
The popular Runners and Racers example that illustrates how to use Hannibal for real projects.

Runners and Racers

So we were just reading an interesting post over at The Disco Blog regarding the simplicity of creating web services using Grails. Grails is a very popular framework for creating web applications. After reading that article we were convinced that creating a web service using Grails was indeed simple; of course we wondered how Hannibal stacked up to that ...

So we decided to implement that "runners and races" example in Hannibal. Here is how you would do it ...

First you need to define your domain objects. Here is the Java interface for a Race domain.

package com.marathonracing.domaininterface;

@hannibal.generator.Presentable()
@hannibal.generator.Persistable(nullable=false)

public interface Race
{
  @hannibal.generator.Persistable(isPrimaryKey=true)
  public String getName();

  public Double getDistance();
}

And then your Runner interface ...

package com.marathonracing.domaininterface;

@hannibal.generator.Presentable()
@hannibal.generator.Persistable(nullable=false)

public interface Runner
{
  @hannibal.generator.Persistable(isPrimaryKey=true)
  public String getFirstName()

  public String getLastName()

  @hannibal.generator.Persistable(nullable=false, minimumValue=13)
  public Integer getAge();

  @hannibal.generator.Persistable(mapTo="Race.name")
  public String getRaceName();

}

So now that you have defined your interfaces for your domain objects you are now ready to view your web application. Hannibal comes with generic handlers that are called whenever a domain does not have on defined by you. To view these generic pages simply type the following at the command line ...

ant hannibal bounce

and stand back. When ant is finished point your browser to GET

http://localhost:8080/marathonracing/a/race

and this will return all available races. Doing a GET (find) to this URI

http://localhost:8080/marathonracing/a/race?h.rep=form

will return a form that you can use to POST (insert) a new race. Posting the form will automatically redirect your browser to GET a form that can PUT (update) an existing race.

To see all the runners in the "CherryBlossom" race simply visit

http://localhost:8080/marathonracing/a/race/cherryblossom/runner

This will return a list of runners. Creating new runners is as simple as creating a new race.

We think this is pretty simple. Understand that along with the above, the autogenerated forms also include free syntactic validation (remember the minimum age of 13 requirement?), as well as free full text indexing for all created resources as well. Full text search is really easy to implement when its done for you.

Hannibal allows you do to more sophisticated things as well, like inject semantic validation and business rules as well as declarative security based on the user (subject), action (HTTP method), and the object (domain). All this while developing in server side JavaScript! Read more of this wiki to get a sense of what else Hannibal gets you.

We think this is very simple indeed.

Runners and Racers Web Services

So how would you create web services? Well Hannibal allows you to define your own handlers. Hannibal can find these handlers automatically as long as you follow convention.

For the Race domain in the "Marathon "script/javascript" directory in the Hannibal project....

marathonracing/handler/race

then create your handler GET_Race.js and place it in that directory.

What would GET_Race.js look like?

var race = h.get();

CONTENT_CONTEXT.content = (h.isDefined(race) === true) ? race.rdfa() : h.msg404();

That's it. You now have a web service that return an RDFa representation of the race. The logic in GET_Race.js is simple. If the race exists then return it, if it is not found then you return a HTTP 404 message. If you go to this URI

http://localhost:8080/marathonracing/a/race/cherryblossom

Here is what the Race domain RDFa fragment looks like

ul class="marathonracing:race">
  <li property="name" class="hannibal:link">
    <a href="" rel="hannibal:self" class="hannibal:string">cherryblossom</a>
  </li>
  <li property="name" class="hannibal:string">cherryblossom</li>
  <li property="distance" class="hannibal:double">5</li>
</ul>

and if you went to this URI (assuming there is no such race as the "monkey" race)

http://localhost:8080/marathonracing/a/race/monkey 

and here is what the 404 RDFa message fragment would look like

<ul class="marathonracing:error">
  <li property="message" type="hannibal:string">Resource Not Found</li>
  <li property="category" type="hannibal:string">404</li>
  <li property="name" type="hannibal:string">a/race/monkey</li>
</ul>

Real simple.

What if you wanted to create a race? Well in the same directory you put GET_Race.js simply create a handler called POST_Race.js.

Here is what it would look like.

var [race, errorMap, key] = h.post();

CONTENT_CONTEXT.content = (race.error === false) ? CONTENT_CONTEXT.redirect(h.prepareUri(key)) : h.sem(errorMap);

Three lines of JavaScript. Real Simple. Do an HTTP post to this URI

http://localhost:8080/marathonracing/a/race

and POST_race.js will be automatically executed by Hannibal.

If you want a better understanding of how this works read the Wiki on CreatingHandlers.

Important!!!

  1. These Hannibal generated classes should never be modified, as Hannibal may overwrite them causing you to lose your modifications! If you wish to enhance these classes, choose to extend (or delegate) them if you wish to enhance the functionality provided.
  2. You should never place your own manually created files in the generated directories, like genjava, gensql, or genphp, since Hannibal may delete these directories during code generation. Instead place all manually created code in the language directories provide by Hannibal, like java, sql, or php.

Sign in to add a comment
Hosted by Google Code