Overview of frameworks

There are so many different technologies out there for achieving the same goal. I'm not even going to try and compare them all here. All I'll quickly point out are some of the merits of each one this project is based on. As I go on I'll get more into the details.

Java JDK 1.5

This release has probably seen the greatest number of new features for a release in the history of Java.

The new features use here are:

  • Annotations. The greatest advantage for this project was the ability to remove Tapestry page and component spec's. Examples:
           // Get a page
           @InjectPage("CrudBasePage")
           public abstract CrudBasePage getCrudBasePage();
    
           // Get a component
           @Component(type = "Border")
           public abstract Border getBorder();
    
           // Define a component
           @ComponentClass(allowBody=true, allowInformalParameters=true)
           public abstract class Border extends CrudBaseComponent {
    
           // Get a handle to a component
           @Component(type = "Shell", bindings = {"stylesheet=assets.styleSheet", "title=literal:crud"})
           public abstract IComponent getShell();
    

    There are many more for Tapestry as documented: http://jakarta.apache.org/tapestry/tapestry-annotations/index.html

  • Enumerations. Logical way to define your own type safe structure. Examples:
          public enum SQLOperatorType {
            EQ("="),
            LT("<"), ...
          }
          
          // and using it:
          switch (getSqlOperator()) {
            case EQ:
                searchExpression = ExpressionFactory.matchExp(crudAttribute.getName(), crudAttribute.getValue());
                break;
            case GT:
                searchExpression = ExpressionFactory.greaterExp(crudAttribute.getName(), crudAttribute.getValue());
                break;  
    
  • Generics. Nail down the types that live in collections
          // Allow only keys on this map to be Strings and the values to be CrudAttribute objects
          private Map<String, CrudAttribute> values = new HashMap<String, CrudAttribute>();
    
  • Autoboxing. No longer need to convert primitive to Object and back
          Integer obj = new Integer(120);
          int i = obj;
    
  • Enhanced for loop.
            List<ObjAttribute> attributes = getAttributes(dataObject);
    
            for (ObjAttribute attribute : attributes) {
                DbAttribute dbAttribute = attribute.getDbAttribute();
                ...
    

    Clearly here there are some excellent new features that are worth capitalising on, expectially the annotations. However this is at the expense of backward JDK compatability.

Maven - How to build, deploy and manage your project

Every project beyond a "Hello World" needs a defined build process. Things like code generation, compilation, packaging and deployment should, once setup, be mostly hidden from the developer and on a day-to-day basis should not have anything to do except execute a certain task. These days any Java developer worth their weight has a decent knowledge of Ant to handle all these tasks. But what if you could use these in a declarative manner - by configuring what you want to do instead of having to write it - and as well as this be able to generate all kinds of useful project information, have your exact dependancies called and documentated, use a common repository... and more as documented here: http://maven.apache.org/maven-features.html

Tapestry - On the web side

Tapestry is a web development framework that is now into version 4. If you had to say one thing about developing with Tapestry you'd have to say that it is component based. Let me demonstrate with an example of getting a date from a user.

Add a date component to your template html:

<form jwcid="@Form" success="listener:formSubmit">
   <span jwcid="@DatePicker" value="ognl:dateOfBirth"/>
   <input jwcid="@Submit" value="Submit form"/>
...

DatePicker is a built in Tapestry component that will display a text entry field with a calendar popup alongside. It is cross browser, looks slick and if you don't like it you can write you own variant!

Link this value to a variable in a Java class, notice above the line ognl:dateOfBirth this will have the effect of calling the matching getter/setter in the Java page.

public abstract class GetDateOfBirth extends BasePage {

    // Tapestry will implement all abstracts for you
    public abstract Date getDateOfBirth(); 
    public abstract void setDateOfBirth(Date dob);
    
    public void formSubmit() {
        log.info("User thinks their DoB is: " + getDateOfBirth());
    }
}    

That is a lot of bang for buck there. For more examples read on and I strongly encourage you to work through Kent Tongs book "Enjoying Development with Tapestry"

Cayenne - Manage your data

There is almost always the need to store data in a persistent way, the problem is we all find it much easier to work with Java objects. Well with Object-Relational frameworks such as Cayenne you can. Cayenne has a rare-these-days but handy GUI to help you model and glue together data and objects. It even lets you reverse engineer existing databases into objects and of course lets you forward engineer your schemas to all kinds of database types and of course to Java objects. Lets work with our users birth date from the Tapestry example.

  1. In the Cayenne GUI we'd create a new data object with a date field and primary key
  2. Generate this into a database table and a Cayenne data object.

    Now we're ready to work in code with the data object, we don't care much in these simple cases about SQL statements, JDBC or databases.

    To create a new object:

      // Get a data context everything revolves around this
      DataContext dataContext = DataContext.createDataContext();
      
      UserDetails user = (UserDetails) dataContext.createAndRegisterNewObject(UserDetails.class);
      user.setDateOfBirth(getDateOfBirth());
      
      dataContext.commitChanges();
    

    To find an existing user object from the birth date:

      List result = null;
      try {
          Expression expression = ExpressionFactory.matchExp(UserDetails.DATE_OF_BIRTH_PROPERTY, getDateOfBirth());      
          SelectQuery query = new SelectQuery(UserDetails.class, expression);
    
          result = dataContext.performQuery(query);
          
          log.info("Found: " + result.get(0))
      } catch (Exception e) {
          throw new DBException("Could not find user who's DOB is " + getDateOfBirth(), e);
      }  
    

    To modify an existing object:

       // Get it as above
       ...
       UserDetails user = (UserDetails)result.get(0)
       user.setDateOfBirth(getDateOfBirth());
      
       dataContext.commitChanges();
    

    To delete an existing object:

       // Get it as above
       ...
       dataContext.deleteObject(user);
       dataContext.commitChanges();   
    

    Of course there is a lot more to it than just that but we've covered off the simple cases.

    Like any framework any of these have their issues, problems and quirks (remember trying to figure out all those compiler errors or stacktraces as a complete newbie?) but once you have learned a few tricks you'll be writing maintainable, scalable and efficient web applications quickly.