Sunday, May 29, 2011

Testing client-server communication with Java Scripting API

I would like to share my best practice for client- and server-side testing with Java Scripting API introduced in Java SE 6. As example I want to test simultaneous JSON serialization / deserialization on both sides. I'm going to use json2 from Douglas Crockford on client-side and Gson on server-side. I want to utilize the class Circle from my previous post and write JUnit tests for its serialization / deserialization. At first we need to implement the same interface by Java and JavaScript. It's convenient to implement an Java interface by script functions or methods. By using interfaces we can avoid having to use the javax.script API in many places.

JsonProvide.java
public interface JsonProvider
{
    public Object fromJson(String json);

    public String toJson(Object object);
}
JsonProvider is an interface which is used later to access correspondent JavaScript methods.

jsonTest.js
var jsonProvider = new Object();

// produces an JavaScript object or array from an JSON text.
jsonProvider.fromJson = function(json) {
    var obj = JSON.parse(json);
    return makeTestable(obj);
};

// produces an JSON text from an JavaScript object or array
jsonProvider.toJson = function(object) {
    var obj = makeTestable(object);
    return JSON.stringify(obj);
};

function makeTestable(obj) {
    obj.getValue = function(property) {
        return this[property];
    };

    return obj;
}

// Test object
var circle = {
    uuid: "567e6162-3b6f-4ae2-a171-2470b63dff00",
    x: 10,
    y: 20,
    movedToFront: true,
    rotationDegree: 90,
    radius: 50,
    backgroundColor: "#FF0000",
    borderColor: "#DDDDDD",
    borderWidth: 1,
    borderStyle: "-",
    backgroundOpacity: 1.0,
    borderOpacity: 0.5,
    scaleFactor: 1.2
};
There are two methods fromJson / toJson and a helper function makeTestable in order to get any value of JavaScript objects from Java. The test object in JavaScript is called circle. The corresponding Java class is called Circle and has the same fields with getter / setter. We can write an JUnit test now.
import com.google.gson.Gson;
import com.googlecode.whiteboard.model.Circle;
import org.apache.commons.beanutils.PropertyUtils;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.io.FileNotFoundException;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

public class JsonTest
{
    private static Gson gson;
    private static ScriptEngine engine;
    private static JsonProvider jsonProvider;

    @BeforeClass
    public static void runBeforeClass() {
        // create Gson
        GsonBuilder gsonBilder = new GsonBuilder();
        gson = gsonBilder.serializeNulls().create();

        // create a script engine manager
        ScriptEngineManager factory = new ScriptEngineManager();
        // create JavaScript engine
        engine = factory.getEngineByName("JavaScript");

        try {
            // evaluate JavaScript code from the json2 library and the test file
            engine.eval(new java.io.FileReader("src/main/webapp/resources/js/json2-min.js"));
            engine.eval(new java.io.FileReader("src/test/resources/js/jsonTest.js"));

            // get an implementation instance of the interface JsonProvider from the JavaScript engine,
            // all interface's methods are implemented by script methods of JavaScript object jsonProvider
            Invocable inv = (Invocable) engine;
            jsonProvider = inv.getInterface(engine.get("jsonProvider"), JsonProvider.class);
        } catch (ScriptException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    @AfterClass
    public static void runAfterClass() {
        gson = null;
        jsonProvider = null;
    }

    @Test
    public void JavaScript2Java() {
        // get JavaScript object
        Object circle1 = engine.get("circle");

        // client-side: make JSON text from JavaScript object
        String json = jsonProvider.toJson(circle1);

        // server-side: convert JSON text to Java object
        Circle circle2 = gson.fromJson(json, Circle.class);

        // compare two objects
        testEquivalence(circle2, circle1);
    }

    @Test
    public void Java2JavaScript() {
        // create Java object
        Circle circle1 = new Circle();
        circle1.setUuid(UUID.randomUUID().toString());
        circle1.setX(100);
        circle1.setY(100);
        circle1.setMovedToFront(false);
        circle1.setRotationDegree(0);
        circle1.setRadius(250);
        circle1.setBackgroundColor("#FFFFFF");
        circle1.setBorderColor("#000000");
        circle1.setBorderWidth(3);
        circle1.setBorderStyle(".");
        circle1.setBackgroundOpacity(0.2);
        circle1.setBorderOpacity(0.8);
        circle1.setScaleFactor(1.0);

        // server-side: convert Java object to JSON text
        String json = gson.toJson(circle1);

        // client-side: make JavaScript object from JSON text
        Object circle2 = jsonProvider.fromJson(json);

        // compare two objects
        testEquivalence(circle1, circle2);
    }

    @SuppressWarnings("unchecked")
    private void testEquivalence(Object obj1, Object obj2) {
        try {
            Map<String, Object> map = PropertyUtils.describe(obj1);
            Set<String> fields = map.keySet();
            Invocable inv = (Invocable) engine;

            for (String key : fields) {
                Object value1 = map.get(key);
                if (!key.equals("class")) {
                    Object value2 = inv.invokeMethod(obj2, "getValue", key);
                    if (value1 instanceof Number && !(value1 instanceof Double)) {
                        // JS number is always converted to Java double ==> only doubles can be compared,
                        // see http://www.mozilla.org/js/liveconnect/lc3_method_overloading.html
                        value1 = new Double(value1.toString());
                    }

                    Assert.assertEquals("Value of property '" + key + "' was wrong converted", value2, value1);
                }
            }
        } catch (Exception e) {
            throw new IllegalStateException("Equivalence test of two objects failed!", e);
        }
    }
}
I create a Gson and a ScriptEngine instances in the method runBeforeClass() and load all needed scripts into the ScriptEngine. After that I get an implementation instance of the interface JsonProvider from the JavaScript engine. Now I'm able to call JavaScript methods from my JsonProvider implementation. There are two tests:

@Test public void JavaScript2Java()

I test here the use case if an JavaScript object (circle) gets converted to an JSON text, sent to the server and converted there to an Java object (Circle). The original JavaScript object and the result Java object are compared afterwards.

@Test public void Java2JavaScript()

I test here the use case if a created Java object (Circle) gets converted to an JSON text, sent to the client and converted there to an JavaScript object (circle). The objects are compared to ensure their equivalence.

You can also use "JavaScript to Java Communication" with Java Scripting API and access Java classes, objects and methods from JavaScript. Pretty cool.

Thursday, May 26, 2011

Auto sized jQuery UI dialogs and PrimeFaces

jQuery (modal) dialog causes some problems in Internet Explorer like scrollbars on page and not working auto width. See e.g. tickets http://bugs.jqueryui.com/ticket/3623 and http://bugs.jqueryui.com/ticket/4437. width:auto is very important for most dialogs because you often don't know how large the dialog's content is (think e.g. about multi-language labels). General you will not get problems with width:auto and can write
<div id="dialog" title="My dialog">
  ... content ...
</div>

$('#dialog').dialog({
    modal: true,
    height: 'auto',
    width: 'auto'
});
or define CSS class
.autoWidthDialog {
    width: auto !important;
}
and write in PrimeFaces
<p:dialog id="dialog" styleClass="autoWidthDialog" header="My dialog">
  ... content ...
</p:dialog>
Working well except one problem - broken dialog's header in IE7 and below. Titelbar is shrunk to the title text length. Unfortunately, jQuery team doesn't want to fix this issue and support flexible dialogs with width:auto, so that a workaround is needed. Annoying scrollbars on page for modal dialogs should be fixed too. I came up with the following solution:

Native jQuery UI dialog:
jQuery("#dialog").dialog({
    modal: true,
    height: 'auto',
    width: 'auto'
    ... other various options ...
}).bind("dialogopen", function(event, ui) {
    // fix for width:auto in IE
    var jqDialog = jQuery(this);
    var parent = jqDialog.parent();
    var contentWidth = jqDialog.width();
    parent.find('.ui-dialog-titlebar').each(function() {
        jQuery(this).width(contentWidth);
    });
    parent.width(contentWidth + 26);
    jqDialog.dialog('option', 'position', 'center');

    // fix for scrollbars in IE
    jQuery('body').css('overflow', 'hidden');
    jQuery('.ui-widget-overlay').css('width', '100%');
}).bind("dialogclose", function(event, ui) {
    // fix for width:auto in IE
    jQuery(this).parent().css("width", "auto");

    // fix for scrollbars in IE
    jQuery('body').css('overflow', 'auto');
});
PrimeFaces dialog built on the top of native one:
<p:dialog id="dialog" styleClass="autoWidthDialog"
          onShow="onShowAutoWidthDialog(this.jq);"
          onHide="onHideAutoWidthDialog(this.jq);">
  ... content ...
</p:dialog>
function onShowAutoWidthDialog(jqDialog) {
    // fix for auto width in IE
    var parent = jqDialog.parent();
    var contentWidth = jqDialog.width();
    parent.find('.ui-dialog-titlebar').each(function() {
        jQuery(this).width(contentWidth);

    });
    parent.removeClass("autoWidthDialog").width(contentWidth + 26);
    jqDialog.dialog('option', 'position', 'center');

    // fix for scrollbars in IE
    jQuery('body').css('overflow', 'hidden');
    jQuery('.ui-widget-overlay').css('width', '100%');
}

function onHideAutoWidthDialog(jqDialog) {
    // fix for auto width in IE
    var parent = jqDialog.parent();
    parent.find('.ui-dialog-titlebar').each(function() {
        // reset titlebar width
        jQuery(this).css('width', '');
    });
    parent.addClass("autoWidthDialog");

    // fix for scrollbars in IE
    jQuery('body').css('overflow', 'auto');
}
I use onShow / onHide client side callbacks. Settings onShow="onShowAutoWidthDialog(this.jq);" onHide="onHideAutoWidthDialog(this.jq);" fix mentioned above IE issues. Unfortunately, PrimeFaces p:confirmDialog doesn't have these attributes (why?), so that I had to add onShow / onHide to p:confirmDialog similar to p:dialog. Component / renderer classes can be easy extended and for JavaScript widget I can post the code if somebody needs it. :-)

Edit: There isn't cross-browser pure CSS solution; I didn't find it and didn't see anywhere.

Monday, May 16, 2011

JSON with GSON and abstract classes

I have switched to Google Gson after many years of using org.json library for supporting JSON data interchange format in Java. org.json is a lower-level library, so that you have to create JSONObject, JSONArray, JSONString, ... and do other low-level work. Gson simplifies this work. It provides simple toJson() and fromJson() methods to convert arbitrary Java objects to JSON and vice-versa, supports Java Generics, allows custom representations for objects, generates compact and readability JSON output and has many other goodies. I love it more and more. The using is simple. Assume, we have a class called Circle.
public class Circle {
    private int radius = 10;
    private String backgroundColor = "#FF0000";
    private String borderColor = "#000000";
    private double scaleFactor = 0.5;
    ...

    // getter / setter
}
Serialization (Java object --> JSON) can be done as follows:
Circle circle = new Circle();
Gson gson = new Gson();
String json = gson.toJson(circle); 
==> json is
{
    "radius": 10,
    "backgroundColor": "#FF0000",
    "borderColor": "#000000",
    "scaleFactor": 0.5,
    ...
}
Deserialization (JSON --> Java object) is just one line of code:
Circle circle2 = gson.fromJson(json, Circle.class);  
==> circle2 is the same as the circle above
Everything works like a charm. There is only one problem I have faced with abstract classes. Assume, we have an abstract class AbstractElement and many other classes extending this one
public abstract class AbstractElement {
    private String uuid;

    // getter / setter
}

public class Circle extends AbstractElement {
   ...
}

public class Rectangle extends AbstractElement {
   ...
}

public class Ellipse extends AbstractElement {
   ...
}
Assume now, we store all concrete classes in a list or a map parametrized with AbstractElement
public class Whiteboard
{
    private Map<String, AbstractElement> elements = 
            new LinkedHashMap<String, AbstractElement>();
    ...
}
The problem is that the concrete class is undisclosed during deserialization. It's unknown in the JSON representation of Whiteboard. How the right Java class should be instantiated from the JSON representation and put into the Map<String, AbstractElement> elements? I have nothing found in the documentation what would address this problem. It is obvious that we need to store a meta information in JSON representations about concrete classes. That's for sure. Gson allows you to register your own custom serializers and deserializers. That's a power feature of Gson. Sometimes default representation is not what you want. This is often the case e.g. when dealing with third-party library classes. There are enough examples of how to write custom serializers / deserializers. I'm going to create an adapter class implementing both interfaces JsonSerializer, JsonDeserializer and to register it for my abstract class AbstractElement.
GsonBuilder gsonBilder = new GsonBuilder();
gsonBilder.registerTypeAdapter(AbstractElement.class, new AbstractElementAdapter());
Gson gson = gsonBilder.create();
And here is AbstractElementAdapter:
package com.googlecode.whiteboard.json;

import com.google.gson.*;
import com.googlecode.whiteboard.model.base.AbstractElement;
import java.lang.reflect.Type;

public class AbstractElementAdapter implements JsonSerializer<AbstractElement>, JsonDeserializer<AbstractElement> {
    @Override
    public JsonElement serialize(AbstractElement src, Type typeOfSrc, JsonSerializationContext context) {
        JsonObject result = new JsonObject();
        result.add("type", new JsonPrimitive(src.getClass().getSimpleName()));
        result.add("properties", context.serialize(src, src.getClass()));

        return result;
    }

    @Override
    public AbstractElement deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
        throws JsonParseException {
        JsonObject jsonObject = json.getAsJsonObject();
        String type = jsonObject.get("type").getAsString();
        JsonElement element = jsonObject.get("properties");

        try {
            return context.deserialize(element, Class.forName("com.googlecode.whiteboard.model." + type));
        } catch (ClassNotFoundException cnfe) {
            throw new JsonParseException("Unknown element type: " + type, cnfe);
        }
    }
}
I add two JSON properties - one is "type" and the other is "properties". The first property holds a concrete implementation class (simple name) of the AbstractElement and the second one holds the serialized object itself. The JSON looks like
{
    "type": "Circle",
    "properties": {
        "radius": 10,
        "backgroundColor": "#FF0000",
        "borderColor": "#000000",
        "scaleFactor": 0.5,
        ...
    }
}
We benefit from the "type" property during deserialization. The concrete class can be instantiated now by Class.forName("com.googlecode.whiteboard.model." + type) where "com.googlecode.whiteboard.model." + type is a fully qualified class name. The following call
 
public <T> T deserialize(JsonElement json, Type typeOfT) throws JsonParseException
 
from JsonDeserializationContext invokes default deserialization on the specified object and completes the job.

Sunday, May 8, 2011

Prevent multiple clicks in JSF

Preventation of multiple clicks on buttons / links is a common and important task during web development. There are many discussions about double click problem in JSF I will try to cover all relevant approaches.

1) Server-side solution.
Server-side solutions are normally based on token concept, phase listeners, filters or other complex tricks. Here is just one example for the server-side preventation of double-form submit Server-side solutions have disadvantages. Every request is handled by a special phase listener, filter or something near it, even if we are not interesting in multiple clicks preventation. There are problems in a cluster environment if session data get replicated. This a real problem I faced many years ago - our server-side solution didn't work proper if the web application was running in a cluster environment. Client-side solutions seems to be simpler and are more reliable.

2) Disable button / link on click.
The simplest client-side solution consists in writing this.disabled=true in onclick. An example:
 
<h:commandbutton action="..." onclick="this.disabled=true;" value="...">
 
I had kinda troubles with this solution and ajaxified buttons like PrimeFaces' p:commandButton - it didn't work. Furthermore the entire button gets frozen and doesn't respond anymore. We want to have a still responding button or link if user clicks on it multiple times.

3) Ajax queue and request delaying.
This is probably the best solution at all. Many popular component libraries already have this solution for the multiple clicks preventation. OpenFaces has e.g. a nice concept in order to avoid frequent requests. OpenFaces' Ajax framework sends only one request in case of several events during the short period of time specified with the "delay" attribute. RichFaces has a4j:queue and a4j:attachQueue. These components have an attribute "requestDelay". This attribute specifies the number of milliseconds to wait before sending a request in order to combine similar requests. The greater the value the fewer requests will be sent when fast typing. Similar requests in the queue are "combined" while waiting for the request delay. This feature results in just one request from a component being sent. For more details see RichFaces showcase and try to do double clicks to see they are "combined" to just one. Unfortunately, but PrimeFaces doesn't have similar feature what would prevent sending of miltiple requests from the same component like buttons / links, datatable filtering on keyup if user types very fast (important for large tables), etc.

4) Using jQuery's bind / unbind methods.
Ajax queue prevents double clicks, but doesn't ensure sending of slow multiple clicks. I have implemented my own solution for PrimeFaces' buttons / links. The idea is to bind onlick handler bind('click', function() {..}) and call unbind('click') within the handler :-) I have also tried jQuery .one() method but it didn't work as desired. We should prepare the renderer class at first. I have these lines there (only short outline for button renderer to get the idea):
writer.write(button.resolveWidgetVar() + 
    " = new PrimeFaces.widget.ExtendedCommandButton('" + clientId + "', {");
...
writer.write("});");
if (button.isPreventMultiplyClicks()) {
    writer.write(button.resolveWidgetVar() + ".bindOneClick('" + clientId + "', function(){" + onclick + "});");
}
ExtendedCommandButton is an extended widget based on the PrimeFaces' one. bindOneClick is a method in this widget and onclick is an onlick handler generated by PrimeFaces for Ajax / non Ajax requests. The extension looks as follows:
PrimeFaces.widget.ExtendedCommandButton = function(id, cfg) {
    PrimeFaces.widget.CommandButton(id, cfg);
}

jQuery.extend(PrimeFaces.widget.ExtendedCommandButton.prototype, PrimeFaces.widget.CommandButton.prototype);

PrimeFaces.widget.ExtendedCommandButton.prototype.bindOneClick = function(clientId, commandFunc) {
    var buttonEl = jQuery(PrimeFaces.escapeClientId(clientId));
    buttonEl.data('commandFunc', commandFunc);
    buttonEl.unbind('click');

    buttonEl.bind('click', function() {
        buttonEl.unbind('click');
        commandFunc();
    });
}
One problem still exists - if user stays on the same page (in case of Ajax updates) and the button is not updated, it doesn't respond more by reason of unbind. We should "reinitialize" it. Therefore I added this method too:
PrimeFaces.widget.ExtendedCommandButton.prototype.reinitialize = function(clientId) {
    var buttonEl = jQuery(PrimeFaces.escapeClientId(clientId));
    buttonEl.unbind('click');

    buttonEl.bind('click', function() {
        buttonEl.unbind('click');
        buttonEl.data('commandFunc')();
    });
}
So, if we navigate to another page (a non-Ajax request), we can write now:
 
<p:commandButton preventMultiplyClicks="true" ajax="false" ...>
 
If user stays on the same page (an Ajax request), a call PrimeFaces.widget.ExtendedCommandButton.reinitialize(clientId) is necessary in "oncomplete" to get multi-click preventation working again. Like this
 
<p:commandButton id="myButton" preventMultiplyClicks="true" ...
                oncomplete="PrimeFaces.widget.ExtendedCommandLink.reinitialize('myButton')">
 
Or you can try to include the button Id to the "update" attribute
 
<p:commandButton preventMultiplyClicks="true" update="@this,..." ...>
 
p:commandLink will be updated and reinitialized again. We can also extend the button renderer a little bit and reinitialize it automatically (if it's not in the Ajax update region). Result: the button / link will be a quite usually PrimeFaces' button / link with a new attribute for multiple clicks. No differences otherwise.
 
<p:commandLink "general PF attributes" preventMultiplyClicks="true">
 

Monday, May 2, 2011

What to do if @PostConstruct is not called for JSF managed beans?

I have read and faced self the problem that methods annotated with @PostConstruct / @PreDestroy get never called. Even in JEE 6 compliant servers (weird). This problem had e.g. Tomcat in early 6.x releases, Jetty 6.1.x, etc. Jetty 6.1.x maven plugin can not be used than during development.

There is a simple solution when using Mojarra JSF. Well, if you use Spring framework, you probably don't have this problem. This post provides a solution for JSF projects without Spring (because it doesn't make sense to use Spring in small, lightweight projects). Since Mojarra 1.2 there is a context parameter for web.xml com.sun.faces.injectionProvider. This parameter specifies a class that implements the InjectionProvider SPI. This implementation represents a hook the JSF implementation will use to provide resource injection support. If no provider, shipped with application server, was found and @PostConstruct / @PreDestroy are not called, we can set the value for this context parameter to com.sun.faces.vendor.WebContainerInjectionProvider as follows
<context-param>
    <param-name>com.sun.faces.injectionProvider</param-name>
    <param-value>
        com.sun.faces.vendor.WebContainerInjectionProvider
    </param-value>
</context-param>
Annotations will be parsed and annotated methods invoked now and you will see a message during application startup.
...
02.05.2011 16:04:13 com.sun.faces.spi.InjectionProviderFactory createInstance
INFO: JSF1048: PostConstruct/PreDestroy-Annotationen available. ...
...
It's also possible to use the JBoss implementaion org.jboss.web.jsf.integration.injection.JBossInjectionProvider. I have never tested it, but I think it would be a proper alternative.
<context-param>
    <param-name>com.sun.faces.injectionProvider</param-name>
    <param-value>
        org.jboss.web.jsf.integration.injection.JBossInjectionProvider
    </param-value>
</context-param>
JBoss 5 and above uses it automatically, so that no needs exist for the mentioned parameter.