Wednesday, August 29, 2012

Keeping "DRY" with Classtemplater

Since I have been starting with REST jax-rs and tests I always had to repeat coding DAOs and Tests. This was extremely unproductive. I have been looking in to Spring Roo and some Eclipse plugins.

Spring Roo was too large and complex and I would have had to re-create the whole project according to the Spring Roo and Maven guidelines. Also I didn't know if GWTP is supported. I didn't want to learn yet another Framework and start from scratch again even if Spring Roo looks very promising and seems to support the creation of GWT. But I just found it to be to complicated and less flexible as I wanted it to be. 

All the other Eclipse Plugins I have been looking at also didn't support all features that I wanted until I stumbled over a plugin called Classtemplater.

Classtemplater is using Apache Velocity which is a scripting language to generate customized configuration files. I have already worked with Velocity and got interested. And..... it is extremely easy and yet f*ing brilliant. Basically the whole code for your DAOs, Tests and and other Classes that rely on to the Entity Class (or any other class but the entity class is the most convenient one) can be generated with Velocity templates.

If you want to get started read the following documentation:
In Classtemplater you define a template that you create from a class. In my example I want to create a CardResourceTests.class based on the Card.class (the entity class that contains all the getter and setter methods and attributes). So you create the Card.class first. Then you create the template called for example resourceTestsCRUD.vm which you put in to a folder in the root of your project. Mine is called "templates".

Once you install the Eclipse Plugin (over Help -> Install New Software -> http://classtemplater.inepex.com) you can right click on to the entity class (in my example Card.class), choose "classtemplator" and click on to "Generate". This will open a menu where you can choose the corresponding .vm template (in my example resourceTestsCRUD.vm).

Classtemplater specific Velocity variables
Print Methods
If you want to get all the methods from a class you are using the .vm template with you can do following:
#foreach( $method in $class.methods )
${method.name} - ${method.returnType}
#end
 
This will print the method name and the return type of all methods.  
 
Print attributes
#foreach( $attr in $attrs )
${attr.name} - ${attr.type}
#end

This will print all the attributes and types of the attributes.

This is actually all you need. The rest is done with Velocity.

Example
In my example we'll be creating a Card.class entity with some attributes. We will use this entity class together with a resourceTestsCrud.vm Velocity template to create the code of the CardResourceTests.class.

Card.class:
import java.util.Date;

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
@PersistenceCapable(detachable="true")
public class Card {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    @Extenstion(vendorName="datanucleus",  key="gae.encoded-pk", value="true")
    private String key;
    
    @Persistent
    private String name;
    
    @Persistent
    private Date creationDate;    
    
    @Persistent
    private Date modificationDate;
    
    public Card() {
        this.setName(null);
        this.setCreationDate(null);
        this.setModificationDate(null);
    }
    
    public Card(String name)
        this.setName(name);
        this.setCreationDate(new Date());
        this.setModificationDate(new Date());
    }

    public Long getKey() {
        return key;
    }

    public void setKey(Long key) {
        this.key = key;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getCreationDate() {
        return creationDate;
    }

    public void setCreationDate(Date creationDate) {
        this.creationDate = creationDate;
    }

    public Date getModificationDate() {
        return modificationDate;
    }

    public void setModificationDate(Date modificationDate) {
        this.modificationDate = modificationDate;
    }
}

resourceTestsCRUD.vm:
package ${package};

public class ${classname}ResourceTests {
    public void testCreate() throws Exception {
        Client client = Client.create();
       
        ${classname} ${classname.toLowerCase()} = new ${classname}();
#foreach($attr in $attrs)   
#if(${attr.name} != "key" && ${attr.name} != "creationDate" && ${attr.name} != "modificationDate")
        ${classname.toLowerCase()}.set${attr.name.substring(0,1).toUpperCase()}${attr.name.substring(1)} = setme(${attr.type});
#end
#end

        WebResource ${classname.toLowerCase()}Resource = client.resource("http://localhost:8888/api/${classname.toLowerCase()}");
        ClientResponse response = ${classname.toLowerCase()}Resource.type(MediaType.APPLICATION_JSON)
            .accept(MediaType.APPLICATION_JSON)
            .post(ClientResponse.class, ${classname.toLowerCase()});
           
        ${classname} ${classname.toLowerCase()}Returned = (${classname})respone.getEntity(${classname}.class);
       
        assertEquals("Status", response.getStatus(), 200);
#foreach($attr in $attrs)   
#if(${attr.name} != "key" && ${attr.name} != "creationDate" && ${attr.name} != "modificationDate")
        assertEquals("${attr.name}", ${classname.toLowerCase()}.get${attr.name.substring(0,1).toUpperCase()}${attr.name.substring(1)}(), ${classname.toLowerCase()}Returned.get${attr.name.substring(0,1).toUpperCase()}${attr.name.substring(1)}());
#end
#end       
    }
}

Now we can right click on to the Card.class -> Choose "classtemplator" -> "Generate" and then choose the "resourceTestsCRUD.vm" template.

The code that appears in the preview that now could be saved as "CardResourceTests.class" looks as following:
package path.to.my.package;

public class CardResourceTests {
    public void testCreate() throws Exception {
        Client client = Client.create();
       
        Card card = new Card();
        card.setName = setme(String);

        WebResource cardResource = client.resource("http://localhost:8888/api/card");
        ClientResponse response = cardResource.type(MediaType.APPLICATION_JSON)
            .accept(MediaType.APPLICATION_JSON)
            .post(ClientResponse.class, card);
           
        Card cardReturned = (Card)respone.getEntity(Card.class);
       
        assertEquals("Status", response.getStatus(), 200);
        assertEquals("name", card.getName(), cardReturned.getName());
    }
}

Sunday, August 26, 2012

AppEngine Datastore REST and Key in Entities

To begin with I have worked with AppEngine Keys but found out during testing that this won't work. The key can't be converted and returned with the jax-rs Response class as in general only Java types (int, string....) can be returned.

Example:
@Path("/card")
public class CardResource {
    CardDAO dao = new CardDAO();

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response create(Card card) {
        Card c = dao.create(card);
        if(c.equals(null)) {
            return Response.status(Status.BAD_REQUEST)
                    .type(MediaType.APPLICATION_JSON)
                    .entity("Create failed!")
                    .build();
        }
      
        return Response.status(Status.OK)
                .entity(c)
                .type(MediaType.APPLICATION_JSON)
                .build();
    }
}

The Response class will return the entity as a JSON object in this case. But the key is returned as "null" value when using the AppEngine Key class.

There are two solutions to this problem:

Solution 1
Work with Long instead of Key. This is fine if you want to use a number as key and don't have to mix it with data that could have an identical key.

Here a sample entity:
import java.util.Date;

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
@PersistenceCapable(detachable="true")
public class Card {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Long key;
    
    @Persistent
    private String name;
    
    @Persistent
    private Date creationDate;    
    
    @Persistent
    private Date modificationDate;
    
    public Card() {
        this.setName(null);
        this.setCreationDate(null);
        this.setModificationDate(null);
    }
    
    public Card(String name)
        this.setName(name);
        this.setCreationDate(new Date());
        this.setModificationDate(new Date());
    }

    public Long getKey() {
        return key;
    }

    public void setKey(Long key) {
        this.key = key;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getCreationDate() {
        return creationDate;
    }

    public void setCreationDate(Date creationDate) {
        this.creationDate = creationDate;
    }

    public Date getModificationDate() {
        return modificationDate;
    }

    public void setModificationDate(Date modificationDate) {
        this.modificationDate = modificationDate;
    }
}

Solution2
Work with Encoded String as Key. This solution is the prefered one as the generated string is a random string that does not conflict with any other id of a different data set if you have to mix the data together with other data created elsewhere.

Here a sample entity:
import java.util.Date;

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
@PersistenceCapable(detachable="true")
public class Card {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    @Extenstion(vendorName="datanucleus",  key="gae.encoded-pk", value="true")
    private String key;
    
    @Persistent
    private String name;
    
    @Persistent
    private Date creationDate;    
    
    @Persistent
    private Date modificationDate;
    
    public Card() {
        this.setName(null);
        this.setCreationDate(null);
        this.setModificationDate(null);
    }
    
    public Card(String name)
        this.setName(name);
        this.setCreationDate(new Date());
        this.setModificationDate(new Date());
    }

    public Long getKey() {
        return key;
    }

    public void setKey(Long key) {
        this.key = key;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getCreationDate() {
        return creationDate;
    }

    public void setCreationDate(Date creationDate) {
        this.creationDate = creationDate;
    }

    public Date getModificationDate() {
        return modificationDate;
    }

    public void setModificationDate(Date modificationDate) {
        this.modificationDate = modificationDate;
    }
}