Tuesday, August 30, 2011

Embedding Images to a Tapestry5 page very easily

OK I'll admit that the title might be problem but then again that's up to you.

Here's the thing, adding dynamic images in Tapestry5 templates is pie. You can read up on it here. But that seems to be best way if you are dealing with a file system or uploaded files. But what if the source is JSON data and doesn't need to be manipulated? Where we'd rather just stick it in directly into the template and display it.

Apparently this is just as easy as pie. First off, I'm dealing with a Base64 string as representation of the image within the JSON data. The image format is JPG, by the way for the just curious. Fortunately, Firefox and Chrome like Base64 strings. Then we can basically do this.

<img src="data:image/jpg;base64,iVBORw0KGgoAAAANS..." />

So what we need then is a webservice that pulls the JSON, process it a bit and pass it into the template.

public interface ICustomerService {

    public JsonObject getCustomerFromWebService(String id);
    
    public Customer getCustomer(String id);
        
    public Map<String, String> getCustomerHistory(String id, String code);
}

The ICustomerService is quite straight-forward. The implementation is where it gets interesting.

//Don't forget to add this into Tapestry5's service registry using binder.bind()    
    @Inject
    private Messages messages;

    JsonSystem system = StandardConfig.createSystem();
    
    public Customer getCustomer(String id) {
        JsonObject obj = getCustomeFromWebService(id);
        Customer customer = new Customer();
        // various gets;
        // lets skip to the interesting parts
        customer.setPhotoB64(obj.getString("ImageB64"));
        return customer;
    }

    public JsonObject getCustomerFromWebService(String id) {
        StringBuilder url = new StringBuilder( messages.get("localhost.mywebservice") );
        url.append(id);
        JsonObject object = system.get(url.toString()).asObject();
        return object;
    }


The first thing here is getting the URL of my web service - which I'm hosting locally. My dilemma was how to do this not in code so if the web service URL changes, I don't have to recompile. My answer was to use the Tapestry5 global properties via the messages catalog. All Tapestry5 web applications have this properties file in the WEB-INF folder. All you need to do is just @Inject the org.apache.tapestry5.ioc.Messages object and then you have now access.

The next bit is using ItemScript. ItemScript is very good JSON toolkit. I think you have noticed that getting JSON data with ItemScript is quite easy. Just create a JsonSystem object and then supply the URL. You should then get a JsonObject which now you can parse for data.

The final parts is just basically now use the new service. We'll start with the Index.java file.

@Inject
    private ICustomerService iCustomerService;
    
    public Customer getCustomer(){
        //let's try out our web service
        return iCustomerService.getCustomer("2033405");
    }
.....


The Index.tml file is just as easy. Add this right after the ${message:greeting} line.

<p>Test Customer</p>
<p> ${Customer.firstname}, ${Customer.lastname}</p>
<img src="data:image/jpg;base64,${Customer.photoB64}" />


Here is a screenshot.
The guy in the image is Aristotle "yoyong" Ucab. He's the guy who wrote the web service.

I don't really know where the heck this photo was taken.

Anyway, have fun with this.